{"service":"auth-onamerica","version":"2.1.0","status":"ok","mesh_role":"Fleet Authorization Service for *.onamerica.org","spec":"GET /.well-known/fleet-auth.json for full integration spec","ventures":[{"code":"link","domain":"link.onamerica.org","customer":"Vets Whole In One"},{"code":"weyland","domain":"weyland.onamerica.org","customer":null},{"code":"mhs","domain":"helmcorp.cc","customer":null},{"code":"iaas","domain":"iaas.onamerica.org","customer":null},{"code":"argo","domain":"deck.onamerica.org","customer":"Argo Consulting LLC"},{"code":"pad","domain":"weyland.onamerica.org","customer":"Precision Auto Doors LLC"}],"endpoints":["POST /api/auth/magic-link — Send 8-digit consent code to email. Unknown email: 404 registration closed unless venture config open_registration=true (then node+guest membership minted first — WRIGHT-2026-0819-WEYLAND-AUTHSTACK-001 R1). The answer carries no name (F23): an unproven request learns nothing about whose address it typed; the name comes back with the proof, in token-status and magic-code node.name","POST /api/auth/magic-code — Redeem an emailed sign-in for a session, in three shapes, each scoped to the venture. { signin } is the sign-in mail's own single-use link token and signs in only the tab that presents it. { email, code } is the shape to use: only that address's newest live code is tried, a wrong code counts against it and the fifth locks it (the right code is refused too). { code } alone is DEPRECATED (A7a-1; answers 400 at A7a-2): it still works, one auth_deprecated line names the route and the caller's X-Venture, and it is throttled per venture (30 failures in 15 minutes, then 429 until the window passes; the other two shapes and passkeys are unaffected). Every failure of every shape answers the same 401 body, so it never says whether an address has a code; only the throttle answers 429. A redemption here never completes a tab that is waiting on token-status (cross-device completion is dropped, OVERWATCH-LINK #9392; the typed code is the cross-device path; binding CROSS_DEVICE_COMPLETION to true brings it back). A proof creates a membership only in an open venture (config open_registration true) or through an invitation roster (a Link registrant with no node is bridged into Link); in a closed venture it creates none and role/:email answers guest with member false (F24)","POST /api/auth/invite — Admin provisions an operator: find-or-create node + venture membership, then SENDS the invite email through the comms boundary (one-click sign-in link; opt out with send_email:false — WS6 S4). Caller must be a verified venture admin (sysadmin to grant admin). (WRIGHT-2026-0819-WEYLAND-AUTHSTACK-001 R2)","GET /api/auth/token-status/:token — Poll for consent confirmation. node.role is the role the person may exercise in the session venture: 'member' for any role but guest and member until the membership is verified, with the role still waiting on verification in node.pending_role and the stored status in node.verification_status. Each tenants[] entry says the same. As with magic-code, a proof creates a membership only in an open venture (config open_registration true) or through an invitation roster; in a closed venture it creates none (F24). A code redeemed through magic-code (used = 4) never mints here: the polling tab is told { consumed: false }, as for a code nobody has entered","GET /api/auth/verify-token/:token — DEPRECATED (A7a-1; answers 410 at A7a-2): one-time code verification. It flips 0→1, which is what the SAME caller's token-status poll completes on, so F4 stays open through this route until it is retired; the sign-in mail no longer links here. One auth_deprecated line names the route and the caller's X-Venture, it is throttled with the code-alone shape (429 past the budget), and every failure answers the one generic body { error, valid: false } with 401","GET /api/auth/role/:email — Resolve role for venture context (INTERNAL ONLY — a service-binding call addressed to host 'internal', or to 'auth-onamerica' until its remaining caller moves; refused for any request carrying CF-Connecting-IP and for any other host; read-only, provisions nothing). role is the role the person may exercise: 'member' for any role but guest and member until the membership is verified, with the role still waiting on verification in pending_role and pending_tier and the stored status in verification_status. Each tenants[] entry says the same","GET /api/auth/staff — List staff for venture. Caller must hold a live session and be a verified admin or sysadmin of that venture.","GET /api/auth/mhs-id/:email — Look up MHS ID. Caller must hold a live session; may look up only their own email unless a fleet operator (a verified sysadmin of venture mhs).","PUT /api/auth/mhs-id/suffix — Self-service MHS ID suffix change","POST /api/auth/logout — Revoke session — invalidate fleet token server-side","POST /api/auth/mint-internal — Mint short-lived scoped service JWT (INTERNAL ONLY — service-binding host contract; allowlisted (service, aud, scope) tuples; two-tier doctrine CH-2026-0818-WEYLAND-PASETO-001)","GET /api/verify/:id/confirm — Trust chain: confirm page — no side effect. Query carries by, exp, t (the signed capability minted into the admin link).","POST /api/verify/:id/confirm — Trust chain: confirm — performs the change. Body carries by, exp, t (the signed capability from the link).","GET /api/verify/:id/followup — Trust chain: follow up page — no side effect. Query carries by, exp, t (the signed capability minted into the admin link).","POST /api/verify/:id/followup — Trust chain: follow up — performs the change. Body carries by, exp, t (the signed capability from the link).","GET /api/verify/:id/deny — Trust chain: deny page — no side effect. Query carries by, exp, t (the signed capability minted into the admin link).","POST /api/verify/:id/deny — Trust chain: deny — performs the change. Body carries by, exp, t (the signed capability from the link).","GET /api/verifications/pending — List pending verifications. Caller must hold a live session and be a fleet operator (a verified sysadmin of venture mhs).","GET /.well-known/fleet-auth.json — This spec (machine-readable)"],"consent_model":{"principle":"Maximum automation between consent gates. Every gate is explicit human affirmation.","gates":["Gate 0: Email entry (identity declaration)","Gate 1: 8-digit code entry (consent to authenticate — code from email, typed into requesting portal)","Gate 2: Biometric opt-in (consent to provision device — first login per device)","Gate 2': Biometric scan (reauthorization — instant access on provisioned devices)"],"token_security":{"generation":"crypto.getRandomValues() — 8-digit random per request","storage":"SHA-256 hashed before INSERT. Plaintext code never stored in D1. Separate polling UUID for frontend.","lifecycle":"Single-use. Prior tokens invalidated on new request. Burns after configurable failed attempts (default 5). Send throttle: up to 10 requests per rolling 30s per email+venture, then a 30s cooldown (CAPT 2026-08-20; was flat 60s between requests).","expiry":"15-minute TTL from creation","transport":"HTTPS only. Code travels via email (MTA relay). User types code into HTTPS page. iOS autocomplete via @domain #code format.","venture_scoped":"Each token is bound to a venture. A GolfLink code cannot be used on Weyland."}},"joining":{"description":"How to onboard a new venture into the fleet auth mesh.","steps":["1. Register venture: INSERT INTO onamerica_ventures (id, code, ...) — ID MUST use ven_ prefix (FTB-2026-0406-001). Example: ven_newventure.","2. Build your auth portal: hascom build auth-portal --venture your_code --redirect /your-app/","3. Deploy the generated index.html to your CF Pages project at your venture domain","4. The portal handles: email entry, 8-digit code, polling, biometric enrollment, session storage","5. All auth API calls go to auth-onamerica.ron-helms.workers.dev — your portal is static HTML","6. Build your venture worker with POST /api/auth/fleet-exchange to accept fleet identity","7. Optional: POST /api/auth/invite for team management, POST /api/consent/grant for consent records"],"why_venture_local_portals":{"reason":"WebAuthn biometric credentials bind to the origin where they were registered. If the auth portal lives on auth-onamerica.ron-helms.workers.dev, credentials bind to that domain — not your venture domain. Each venture deploys its own static copy so biometric enrollment binds to the correct origin.","build_command":"hascom build auth-portal --venture X --redirect /path/","output":"Static HTML baked with venture constants at build time. Same template, different origins.","update":"hascom build auth-portal --all rebuilds every venture from the latest template + D1 config."},"venture_worker_contract":{"description":"Your venture worker should implement these routes to integrate with fleet auth:","routes":["POST /api/auth/fleet-exchange — accept fleet identity, create/match local node by mhsId, return venture-scoped context","GET /api/node/ventures — return ventures for authenticated node (populates workspace switcher)","GET/POST/DELETE /api/ventures/:id/members — team management"]},"consent_constraints":{"rule":"Every cross-system operation requires consent from both parties. Fleet auth publishes identity. Your service subscribes to it. Neither stores the other's credentials.","token_chain":"Fleet identity → Venture context → API calls. Each hop is a consent boundary.","consent_inline":"Consents travel WITH the identity in the token-status response. No extra call to consent-onamerica needed for enforcement.","guest_access":"Pre-consented view-only tokens bypass fleet auth. Your service issues these directly."},"compliance":{"encryption":"All D1 data encrypted at rest. Tokens SHA-256 hashed. MTA secrets in CF vault. WebAuthn keys never leave device.","soc2":"Auditable consent chain in onamerica_ops_log. Configurable brute-force attempt limits per venture.","gdpr":"GET /api/user/export for data access. DELETE /api/user/erase for erasure cascade. POST /api/consent/withdraw for consent withdrawal. 13 consent types managed via consent-onamerica."},"migration_system":{"description":"Schema changes are tracked via hascom migrate. Numbered SQL files in hascom/migrations/. Each migration gets a chain block.","commands":"hascom migrate --status | --history | --plan | --create NAME"}}}