Skip to content

test(dev): live ceremonies for GitHub, X and Google from the dev app - #72

Open
SupremaLex wants to merge 108 commits into
mainfrom
feat/e2e-bind
Open

SupremaLex wants to merge 108 commits into
mainfrom
feat/e2e-bind

Conversation

@SupremaLex

@SupremaLex SupremaLex commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Live end-to-end tests that prove real GitHub, X and Google identities from the dev app's UI, against the real platforms, notary and Bridge. No chain is involved.

What it adds

  • Live specs in ts/apps/dev/e2e/, run with pnpm --filter @libid/dev test:live (playwright.live.config.ts). The config starts the dev app's own stack (pnpm dev), clicks the platform in the UI, drives the popup through the platform's sign-in and consent, and checks the run is accepted for the expected platform and account. Proof contents are left to @libid/ceremony, which validates them before the app sees accepted.
  • Platform drivers:
    • github.ts signs in with username, password and TOTP, one code per 30-second step and each submitted once;
    • x.ts and google.ts restore a saved session and only consent;
    • popup.ts drives the popup and notices its return to the Bridge, or the app's own failure.
  • Settings (settings.ts): the same variable names as libid-server-rs' ceremony-tests, so one .env.test serves both suites. LIBID_LIVE_PLATFORMS lists the platforms to prove, and each one listed must have its variables. The README has the table.
  • Dev app:
    • local.ts holds the app, Bridge and notary origins;
    • services.ts refuses to start when the stack's ports are already in use.
  • Secrets stay out of the stack: the stack gets an allowlisted environment, so the build, Bridge, notary and CCDP never see the test accounts' secrets.

Google's session: a manual first step

Google challenges a saved session used from an address other than the one it was signed in from. It also refuses sign-in in an automated browser. So CI never signs in to Google; it reuses a session a person made:

  1. Open the proxy CI uses on your machine with an SSM port-forward (aws ssm start-session … AWS-StartPortForwardingSession).
  2. Sign in to the test account by hand, in plain Chrome with a fresh profile and --proxy-server pointing at that tunnel.
  3. Close Chrome and export the profile's session with node e2e/export-session.ts <profile> google.json.
  4. Store base64 google.json as the GOOGLE_TEST_ALICE_COOKIES secret, and delete the file and the profile.

Steps are in ts/apps/dev/README.md ("Google's session"). Redo them when the Google job reports an expired session, or when the proxy's address changes. X's session is likewise saved once (ceremony export x); GitHub signs in on every run.

CI (.github/workflows/live-e2e.yml)

Not a required check.

  • When: it runs daily, on dispatch, and on same-repository pull requests touching the dev app, its packages or the workflow. Fork and Dependabot PRs are excluded.
  • GitHub and X job: runs on a GitHub-hosted runner.
  • Google job: runs only on main, and only while LIBID_GOOGLE_RUNNER=ssm. It assumes LIBID_GOOGLE_PROXY_ROLE over OIDC, opens an SSM port-forward to a Squid proxy on an EC2 instance (LIBID_GOOGLE_PROXY_INSTANCE) with no inbound ports, and sends the browser through it, so Google sees the address the session was signed in from.
  • Concurrency: each test account has its own concurrency group, so two runs never submit the same TOTP code.
  • On failure: a job prints the end of the stack log to the job log, where GitHub masks the secrets, and uploads the screenshots.

The proxy's Terraform currently lives outside this repository (a sandbox AWS account). Where it is deployed for good is still open; the workflow only needs the two variables and the role's OIDC trust.

Verified

  • CI on this branch is green, including the Google job through the proxy, for example run 39938543539.
  • GitHub's identity notarization took 5–10.6 s on runners, which is why it needed fix(ceremony): give a notarized request 30 seconds #165's 30-second budget.
  • Locally, typecheck, lint, format, the unit tests and the dev mock suite pass.

Known limits

  • person.ts gives the X and Google browser a desktop user agent and installs stealth.js for the whole context, so the app and prover pages run with it too.
  • A partitioned cookie's cross-site flag is not restored, because Playwright's public API cannot express it.

Runs the CCDP ceremony against the real Bridge, notary and CCDP, encodes
the accepted proof as the GitHub verifier's TlsNotaryProof payload, and
submits IdentityRegistry.bind from the user's wallet. It then reads the
binding back from the registry and from the indexer. stack.ts starts
anvil with the real libID-contracts v0.15.0 stack deployed by
libid-deploy, a notary signing with the key that stack trusts, Bridge,
CCDP and the usernames indexer.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The live test signs in to GitHub with the test account (password and
TOTP, ported from libid-server-rs' live ceremony suite), lets the
ceremony notarize and prove in the browser, has an injected EIP-1193
wallet backed by an unlocked anvil account submit bind, and checks the
transaction, the registry and the indexer through the page. Anvil mines a
block every second so block time follows the clock: a proof's notary
timestamp is checked against it.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Biome skips the app's build cache as it does apps/dev's. The Bind GitHub
anchor drops role=button, so it reads as the link it is. The GitHub login
loop is split into one handler per page, under oxlint's complexity limit.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
pnpm -C ts/apps/bind test:live now starts the stack and app through
Playwright's webServer, runs the test and stops them; the stack saves its
container logs when LIBID_STACK_LOGS names a file. The Live end-to-end
workflow runs it with the GitHub test account from repository secrets,
fails rather than skips when they are missing, and keeps the trace, video
and logs of a failed run.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The app binds X as it binds GitHub: both are TLSNotary ceremonies with
the same payload. The X test restores the saved session libid-server-rs'
ceremony export writes, presents the browser as a person's Chrome with
the stealth script and client hints ported from that suite, and approves
the OAuth app. The live workflow passes the X secrets.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The stack runs the keeper once after the deploy, so GoogleJwtRoots
trusts Google's current keys through a notarized MPC-TLS reading; the
notary opens its wire port for it. The app encodes the Google
verifier's payload, whose client identifier is the token audience the
result carries on its identity. The test restores the saved Google
session and consents in the popup; X and Google share one saved-session
loader. The nightly workflow takes the Google secrets.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Pull requests from this repository get its secrets, so the suite can
run on them as well as nightly; a fork's pull request is skipped.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The runner context is not available in job-level env, which made the
whole workflow file invalid.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Keep both sides of the Biome ignore list, and regenerate the lockfile
from main's with the bind app's dependencies.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
client.connect now generates the connection ID from a popup opened by
PopupWindow.fromAnchor, and client.new takes no ceremony ID.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Main's ceremony proves with the libid-circuits v0.6.0 circuits, whose
verifiers chain-configurations v0.15.0 deploys. Under 0.14.0's
verifiers every bind reverts with SumcheckFailed.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://feat-e2e-bind.previews.lib.id, https://feat-e2e-bind-libid.grounded-systems.workers.dev (commit d3d0f35)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://9fb5cb68.previews.lib.id, https://9fb5cb68-libid.grounded-systems.workers.dev d3d0f35 2026-10-22T16:26:53.403Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://4276c645.previews.lib.id, https://4276c645-libid.grounded-systems.workers.dev b802eea 2026-10-19T16:55:50.656Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://5d795c04.previews.lib.id, https://5d795c04-libid.grounded-systems.workers.dev 7bf9e06 2026-10-19T14:43:11.557Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://d52e8c8a.previews.lib.id, https://d52e8c8a-libid.grounded-systems.workers.dev 4b2f2c9 2026-10-05T10:03:06.055Z Visit the dashboard ↗

The proof wait ends as soon as the page reports the ceremony failed,
with its message, instead of running out its five minutes. Google's
verification pages are caught by their challenge path and by text with
either apostrophe.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Google challenges the saved session when it arrives from a runner's
address, so the test fails on every run there. It stays in the suite
for local runs, and the workflow no longer reads the Google secrets.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
A Playwright trace records every fill and cookie, so the public
artifact of a failed run held the GitHub test password and the saved
sessions. CI records no trace or video, and a step refuses the upload
when any file holds the password, the TOTP secret or a saved X cookie
value.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- local.ts holds the stack's addresses and ports for the stack, the app,
  the Vite and Playwright configs and the spec.
- e2e/popup.ts drives a platform's pages until the popup leaves them,
  for GitHub, X and Google alike, and clicks a page-marked control.
- LIBID_LIVE_PLATFORMS selects the platforms a run binds; CI binds GitHub
  and X, and the stack rotates Google's keys only when Google is bound.
  liveSecret fails a missing secret under LIBID_REQUIRE_LIVE everywhere.
- The page reports one outcome, and the test waits on it instead of a
  hand-written loop that recognised only one terminal stage.
- stack.ts keeps one chain client, drops a code check quoteBind already
  implies, takes needsRotation's ABI from @libid/contracts, and waits for
  Bridge and the indexer while the deploy runs.
- The failure artifact uploads only screenshots, error notes and the
  stack log, after scan-secrets.ts finds no secret in them.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…arted with

- quoteBind and bind route on VERIFIER_VERSION, the CeremonyProofVerifier
  slot libid-deploy registers every launch verifier at (its
  LAUNCH_VERIFIER_VERSION, 1), instead of the ceremony version the proof
  reports. The two are different numbers that happen to agree at launch.
- submit() takes the wallet and holder captured when the ceremony started,
  the holder its transaction data names, instead of re-reading the
  module's current ones.
- The popup connection closes however the run ends, a rejection included.
- A failed start reports its own error message rather than always blaming
  the Bridge configuration.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- Before `up`, `compose down --volumes --remove-orphans` removes what an
  interrupted run of this checkout left, so the chain and the indexer's
  database start empty.
- With any port the stack publishes (anvil, notary, Bridge, indexer API)
  still taken after that, startup refuses: the anvil check could pass
  against another checkout's chain and the deploy would go to it.
- Once the stack is stopping, every wait rejects and no later step runs:
  no deploy, keeper or Vite server after Compose has exited; a Vite server
  created during a stop is closed instead of listening.
- `down` reports when Docker cannot run it.
- The deploy check also requires a verifier at VERIFIER_VERSION for every
  platform the run binds.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
After GitHub's error page the driver went back a page and cleared its
"login filled" flag, and leaving github.com counted only with that flag
set. The session was already signed in, so no login form came to set it
again, and the redirect was never taken for success; going back could
also leave github.com without any redirect.

As in libid-server-rs' github.rs, the error page now reopens the
authorization request the run started on, so the popup leaves github.com
only through GitHub's redirect, and leaving it is success. The popup
driver loses its `done` hook, which only GitHub used.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- Dependabot's pull requests get no secrets; the job skips them as it
  skips forks.
- Checkout keeps no credentials.
- The test step has its own 45-minute timeout (stack start 15, two tests
  of 12, stop 2), inside a 55-minute job, so a hung run still reaches the
  secret scan and the upload. A test's timeout is 12 minutes: the popup
  driver's 3 and the page's 8-minute outcome wait.
- The scan runs when the test step failed or timed out, not on a
  cancelled run, and the upload follows a clean scan.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- drivePopup succeeds only once the popup requests Bridge's OAuth callback
  (local.ts BRIDGE + /auth/callback), seen as a navigation request so an
  immediate redirect onward still counts. Leaving the platform for another
  page keeps it waiting until its budget names the page; a Chrome error
  page or a popup closed before the return fails at once.
- Waits inside a driver go through `pause`, which rejects with the
  driver's own reason when the wait would pass the 180 s budget, so
  GitHub's rate-limit and error-page backoffs report themselves instead
  of a generic timeout. The budget and the page's 8-minute outcome wait
  fit the 12-minute test timeout.
- The page text reaches every driver lowercased with typographic
  apostrophes as plain ones; Google's own copy of that goes.
- GitHub's authorize click has a 5 s timeout, ignores a failed click as
  X and Google do, and is not repeated within 5 s; the two-factor goto
  and the rate-limit reload ignore their errors, as github.rs does.
- Session renewal hints say where libid-server-rs' `ceremony` lives:
  branch feat/live-ceremony-tests, libid-server-rs PR #12. stealth.js
  names presentAsPerson, which installs it.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
… agree on

- The outcome is "bound" only when the registry resolves the identity to
  the holder the run started with and the indexer reports that holder;
  any other holder, or no indexer answer within 60 s, is a failure with
  its own message.
- A ceremony that fails to start sets the outcome too, so the test ends
  on its message instead of waiting out the outcome timeout.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- stack.ts takes .cache/stack.pid before touching the Compose project and
  refuses while another live stack.ts holds it, so a second run no longer
  tears down the first; a lock whose process is gone is stale. The lock
  also covers the CCDP build, which the running ccdp container mounts.
- With LIBID_STACK_LOGS set, every child (the CCDP build, Compose with the
  containers' logs, libid-deploy, the keeper) writes to that file as it
  runs, not to our output, and the end-of-run `compose logs` goes. Under
  CI the Playwright web server drops the stack's stdout, so the public job
  log carries none of it.
- Compose pulls every image, the keeper's included, before anything is
  timed, so a cold pull no longer eats the anvil wait.
- libid-deploy is chosen by host (Linux or macOS, x86_64 or aarch64), each
  archive pinned by SHA-256, and a failed download reports its status
  instead of a digest mismatch.
- Startup fails fast when local.ts, compose.yaml or keeper.toml disagree
  with local-dev.toml's [contracts] addresses, or bridge-config.toml does
  not admit local.ts' app origin.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- Each pull request has its own concurrency group, a newer push cancelling
  the older run; scheduled and dispatched runs share another. A single
  group let one PR's pending run cancel another's.
- Before the scan, any libid-bind Compose project still up is taken down,
  and the scan and the upload use one copy of the stack log, so nothing
  written after the scan goes up.
- The artifact comment says what the scan does: it reads bytes, so a
  screenshot is safe because the pages driven never display a secret, and
  the job log carries none of the stack's output.
- The X session comment says where `ceremony export x` lives.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
An AbortController replaces the stopping flag: waits end on it and
commands the stack runs are terminated by it, so the startup sequence
reads straight through instead of re-checking a flag between steps.
The deploy tool downloads and CCDP builds while Docker clears the old
stack and pulls, with the clean-up and the pull side by side. One
helper probes Bridge and the indexer API, the ports are probed at once,
the Bridge address comes from local.ts, the archive is unpacked from
memory, and one fail() reports a refused start.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The X and Google saved sessions live in session.ts, where the secret
scan reads the same definitions the tests restore. clickFound owns the
data-libid-click marking, so each driver gives only the control to
find. GitHub's error-page and rate-limit back-offs share one helper,
SameSite maps through a table, and the app reports a failed run through
one fail().

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
@SupremaLex SupremaLex changed the title test(bind): end-to-end GitHub binding from UI to chain test(bind): live end-to-end binding for GitHub, X and Google, from UI to chain Oct 6, 2026
ts/apps/bind becomes ts/apps/ceremony-live and @libid/bind becomes
@libid/ceremony-live, ahead of the app dropping the chain: what it runs
daily is the CCDP ceremony, which the ceremony package does not yet hand
to `bind`. The Compose project prefix, the workflow's paths and biome's
ignores follow. No behaviour changes.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- Under CI the scan fails when it has no secret to look for, as a
  failure-output step given none of the job's secrets would.
- settings.test.ts checks that each job's failure-output step is given
  every scanned secret (SCANNED) its test step is.
- scan-secrets.test.ts runs the scan on logs holding fake markers: plain,
  URL-encoded and JSON-escaped values, a localStorage value, a session
  set but undecodable, the 8-character minimum, a clean log, and CI with
  nothing to look for. The dev test script runs every e2e/*.test.ts.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…rows

.gitignore ignores .env.test, *.env.test and .env.*.local everywhere;
the README says to keep the env file outside the repository or name it
.env.test, and describes LIBID_STACK_LOGS (appended, its path rule,
required under CI), CI's effect on the job log, and an empty
LIBID_LIVE_PLATFORMS.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- paths.ts holds the path rule (a relative path is the env file's
  neighbour, else relative to where pnpm was invoked); settings.ts and
  services.ts both resolve through it, so LIBID_STACK_LOGS means the
  same file in `pnpm dev` and `test:live`.
- A Lookup returns a value as set, empty included; settings.ts treats
  empty as absent itself, so an injected lookup can express "set but
  empty" for LIBID_LIVE_PLATFORMS without reading process.env.
- loadEnvFile: an exported value wins over the env file; an exported
  empty value counts as unset, except LIBID_LIVE_PLATFORMS, where it
  selects no platform.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- The scan read sessions with its own parser, which missed the
  capitalised `Value` spelling cookies.rs accepts: a log holding such a
  value passed. It now reads every cookie through session.ts'
  exportCookies, with no host or expiry filter, plus storage-state
  localStorage values; text that is no export falls back to its
  "value" strings, the key in any case.
- The scanned variables are settings.ts' SCANNED, one list; a test sets
  each in turn and checks the scan finds its marker, and that a variable
  outside it is not looked for.
- A found secret exits 2 and nothing to look for under CI exits 3, so
  the tests tell them from a crash (1); the workflow fails on any.
- storedOf checks the optional fields' types as cookies.rs does (path,
  secure, httpOnly, sameSite, expires, partition key): a wrong type
  makes the export unreadable, never a TypeError.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…t rule

- The TOTP step reads the clock once for its step and code, and counts
  its form showing again as a refusal only 5 s after the submit and in a
  later step, so a slow redirect is not a refusal.
- GitHub pages route by their known paths before the 2FA checkup's text,
  which the TOTP page could otherwise match.
- The X driver recognises X's pages with session.ts' onX, the Rust
  suite's x_host; cookies are still set on x.com and twitter.com, as the
  Rust suite sets them.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- watchAppFailure lets the spec tell drivePopup when the app's run has
  failed; the driver then stops with the run's own message instead of
  waiting out its budget on the platform.
- The spec uses app.fixtures.ts' launch, newestRun and a new expectReady
  (openApp uses it too), and watches popups for their return with a
  context 'page' listener added before the click, not a waitForEvent
  predicate with a side effect.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- preflight() runs in the runner process at config load: the settings,
  then under LIBID_REQUIRE_LIVE each selected platform's saved session
  and the GitHub TOTP key, so a bad one fails in seconds.
- With no platform selected the config starts no webServer.
- With LIBID_STACK_LOGS the webServer appends the build's output to it
  too, so under CI a failed build is in the scanned, uploaded log rather
  than lost with the ignored stdout.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Each job's timeout-minutes must be at least its test step's plus a
stated 10 minutes for checkout, install, the browser install, any proxy
and the failure-output step.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- Both live jobs share a job-level concurrency group across all runs
  (live-e2e-accounts, never cancelled): overlapping runs could submit the
  same TOTP code within one 30-second window. Pull request runs still
  cancel their own older run. GitHub keeps one pending job per group, so
  a newer pending job replaces an older pending one; the comment says
  so.
- Pull requests trigger the workflow only for what a live ceremony runs:
  the dev app, the ceremony, popup and ledger packages, their build
  script and brand/ styles, the workspace setup, and the workflow and its
  two actions.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- The live webServer's environment sets every account and session
  variable (settings.ts' table, outside tooling) to empty: services.ts,
  the build and the containers get none of the test accounts' secrets.
  The secret scan stays as a backstop.
- The stack starts only when a test will run: a selected platform whose
  settings are all present (runnablePlatforms). A run that skips every
  test neither builds nor starts it.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
A *_COOKIES_FILE the scan cannot read now fails it (exit 4), naming the
variable but not the path or anything in it, instead of being skipped.
The scan also loads the file LIBID_TEST_ENV_FILE names, as the config
does, so a session file set there is scanned and resolved by the same
path rule. Both have tests.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- The failure-output action stops the stack best effort: a failed
  `docker compose down` no longer aborts the step before the scan and
  the upload. The scan's failure stays fatal.
- `node services.ts --project` prints the checkout's Compose project,
  which the action stops, instead of repeating its libid-dev prefix.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
With --app, services.ts also probes APP_ORIGIN's port before starting,
beside compose.yaml's published ports.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
A saved Google session holds a session only with LSID or __Host-1PLSID
on accounts.google.com, as libid-server-rs' google.rs signed_in has it,
rather than SID or __Secure-*PSID; preflight checks it the same way.
The X rule stays x.rs' signed_in: an auth_token.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
As the Rust driver does, "Something went wrong" on a page other than
the consent, login or /account/access ones fails the run, naming the
path. X holding the account at /account/access for more than 30 s, the
Rust suite's wait, fails with a named error.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- When the code's POST target, /sessions/two-factor, shows a failure,
  the driver keeps GitHub's flash message before going back to the code
  form, and the refusal error carries it.
- After filling six digits the driver waits up to 3 s for GitHub's own
  submit and presses the button only if the form is still there, so one
  code is not sent twice.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- drivePopup races the app-failure check, as well as each step, against
  its budget.
- budgets.ts lists the bounded steps one test performs (page load,
  Ready, the launch click, the popup, the run's id, the driver, the
  outcome, the row's message): 15 minutes; TEST is 16. settings.test.ts
  requires TEST to exceed their sum, and the CI step timeouts' comments
  follow (60 and 28 minutes, within 65 and 35).

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The live tests had grown guards and drift checks around a small core.
This keeps the core and drops the rest:

- settings.ts: each platform's variables (the _COOKIES/_COOKIES_FILE
  alternates included), the env file, LIBID_LIVE_PLATFORMS checked, one
  "missing" error under LIBID_REQUIRE_LIVE, and the runnable platforms
  that decide whether the stack starts.
- session.ts: decode a saved session without echoing the parser's
  message, keep the platform's hosts, check one signed-in cookie per
  platform (X auth_token; Google LSID or __Host-1PLSID on
  accounts.google.com), and map to addCookies.
- The stack's environment is an allowlist (PATH, HOME, USER, TMPDIR,
  LANG, CI, PNPM_HOME, XDG_*, DOCKER_*; Playwright merges its own, so
  everything else is set empty): no test account's secret, the Rust
  suite's passwords included, reaches the build, services.ts or the
  containers.
- github.ts: one TOTP per 30-second step, each code submitted once, the
  login filled once, the 2FA checkup skipped; the reference-vector test
  stays.
- services.ts: LIBID_STACK_LOGS (appended) and the refusal to start on a
  taken port, compose's published ones and the app's.
- The X and Google drivers are back to their first port; X pages are
  recognised with session.ts' onX.

Removed: the secret scan and its tests, preflight, the shared path and
budget modules, the settings/README/workflow drift tests, the typo
warning, the notary health probe, services.ts --project and its
compose-port checks, per-field cookie validation and the __Host- url
branch.

BREAKING CHANGE: LIBID_STACK_LOGS is no longer required under CI, and
the scan-secrets script is gone.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Two jobs with plain steps: GitHub and X in one, Google through the SSM
proxy in the other, each given only its platforms' secrets and its own
concurrency group, so neither cancels the other's pending run. On a
failure a job stops any stack left running, prints the end of the stack
log to the job log, where GitHub masks secrets and the stack holds none,
and uploads the screenshots for 7 days. The failure-output action and
its scan are removed. Each job states its timeout arithmetic.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The drivers' injected page scripts give way to locators: X's consent
button by test id or role, its cookie banner by role (tried until it is
accepted), Google's account row by data-identifier or its Continue or
Allow button, and Google's login check by visible inputs. One
throttledClick in popup.ts, at most a click every 5 s on the first
visible match, replaces GitHub's, Google's and X's own timers. The
app-failure check runs every 2 s. TOTP generation keeps only the
options otpauth does not default, and the renew messages drop the
branch name.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- LIBID_LIVE_PLATFORMS set means every listed platform must have its
  variables (one error names those missing); unset, every platform with
  its variables runs and the rest are skipped. LIBID_REQUIRE_LIVE is
  gone. Alternate variable names are lists, not an `A|B` string.
- Saved sessions restore with a plain map: no twitter.com copy of X's
  cookies. A partition key in the Rust export's CDP object form maps to
  its top-level site.
- The spec checks the run is accepted for the expected platform and
  account; the ceremony already validates the proof, the nodes, the ids
  and the expiry. The row's message is read only for a run that was not
  accepted.

BREAKING CHANGE: LIBID_REQUIRE_LIVE is removed; set LIBID_LIVE_PLATFORMS
to require platforms.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
- With LIBID_STACK_LOGS the live webServer command redirects all of its
  output there (`exec >>"$LIBID_STACK_LOGS" 2>&1`), the build's
  included; services.ts writes to its inherited output again, and the
  one path rule stays settings.ts'.
- services.ts no longer probes the app's port: Vite's strictPort and
  Playwright already refuse a taken one.
- local.ts gains APP_PORT and BRIDGE_CONFIG, used by vite.config.ts,
  services.ts' readiness check and app.fixtures.ts; app.spec.ts and
  app.ts use NOTARY and BRIDGE directly.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The failure step only prints the end of the stack log: the test step
stops its own stack. LIBID_REQUIRE_LIVE is gone (each job lists its
platforms), both jobs exclude forks and Dependabot the same way, and
each job's timeout carries its arithmetic in one comment. The README
follows, and .gitignore drops a pattern *.env.test already covers.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Pull requests no longer run it; the proxy role trusts this workflow on main only.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Google trusts a saved session only from the address it was signed in from, so it is made by hand in plain Chrome through CI's proxy and exported with e2e/export-session.ts; the expired-session error points there.

Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
@SupremaLex SupremaLex changed the title test(bind): live end-to-end binding for GitHub, X and Google, from UI to chain test(dev): live ceremonies for GitHub, X and Google from the dev app Oct 23, 2026
@SupremaLex
SupremaLex marked this pull request as ready for review October 23, 2026 15:49

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant