Technical · Raw markdown

Sign-in wire

Source: pcoc at commit aaa8946. Assumes: an object your verifier has measured (Verifier and policy). Next: run the door yourself (Admission gate).

  1. The flow. The website mints the link https://groundinglab.io/sign-in/?v=1&nonce=<64hex>&exp=<dec>. The client signs and POSTs once to /v1/sign-in. The website polls GET /v1/sign-in/result/{nonce}. The website owns the session; the Gate mints none.
  2. What the client signs. challenge_bytes = pcoc-gate-signin-v1 ‖ 0x00 ‖ len ‖ origin ‖ nonce(32) ‖ exp u64 BE ‖ object(32). That's 116 bytes when the origin is https://groundinglab.io (22 characters), and 113 bytes when the origin is https://example.com (19 characters). The BIP-340 message is SHA-256 of it, hashed once. A signature is in time when now < exp+5 and exp <= now+125 (a 120 s lifetime plus 5 s skew). Vector: sign-in.json.
  3. The body has six keys: origin, nonce, exp (an integer), object (sha256 hex), object_b64 (padded std base64), signature (128 hex). A content type other than application/json is HTTP 415 and writes no website outcome.
  4. What the client sees. The client sees one of three bodies: {"delivery":"ok"}, or HTTP 400 with {"error":{"code":"bad_signature"}} or {"error":{"code":"malformed"}}. Delivery OK is every completed response other than those two HTTP 400 bodies, including HTTP 413, 415, 429, and 503. The client shows Sign-in submitted for delivery OK. So a wrong content type gets HTTP 415, and the client counts that as delivery OK.
  5. Poll and outcomes. The website polls for one of these result tokens: pending, accepted (+public_key, object_sha256, legs=N, span_s=S, and log_day and log_seq naming the decision-log line once it is written; each extra field is present only when set), malformed, bad_signature, too_short, rejected, earlier_version, too_few_waypoints_anchored (this build doesn't emit it). earlier_version is the website outcome for an earlier-version object; the verify job returns error.code earlier_version for the same case. There's a nine-step check order. Rows last 600 s after settling.
  6. Choosing a door. The hosted door for the example deployment, https://admission.groundinglab.io, is opt-in (apps/android/README.md); you can run your own Gate (Admission gate). Signed origin. Any site may run a gate. The signed origin is the requesting site's own web origin, compared byte for byte (SPEC §15.1 Origin, spec-v2.0.3). https://groundinglab.io is one example deployment. Each signing client is built for the deployments it serves: the Sign-in door URL for each accepted origin is part of that client's build. A client must not derive or guess a door from the origin, and must refuse a link whose origin has no configured door (SPEC §15.1 Submit). The reference Gate fixes its accepted origin to https://groundinglab.io (src/wire.rs ORIGIN); there is no setting for it. Any other value gets malformed at check-order step 2. Separately, allowed_origins and extra_origins are browser CORS settings: they govern which HTTP Origin may call the Gate, not the signed origin above. Both are empty by default, so only loopback origins are allowed (src/config.rs; src/http.rs origin_allowed).
  7. Routes (src/http.rs:84-97):
    • GET /v1/health
    • GET /v1/policy
    • POST /v1/verify
    • GET /v1/verify/{job_id}
    • POST /v1/sign-in
    • GET /v1/sign-in/result/{nonce}
    • GET /latest.json
    • GET /log/{YYYY-MM-DD}/{seq}
    • GET /log/{YYYY-MM-DD}.jsonl Every route also answers OPTIONS (CORS preflight); anything else is 404 not_found.

Checked against pcoc aaa8946 (services/admission-gate/WIRE.md). Raw markdown.