{"service":"auth-onamerica","version":"2.0.0","mesh_role":"Fleet Authorization Service for all MHS ventures on *.onamerica.org","base_url":"https://auth-onamerica.ron-helms.workers.dev","ticket":"CH-2026-0402-HASCOM-001","identity_model":{"description":"One email, one person, many ventures. Universal identity with sovereign MHS ID.","mhs_id_format":"MHS-{seq}.{variant}-{suffix} (e.g. MHS-00001.A-CAPT)","taxonomy":"Node = person (MHS ID). Venture = platform (ven_). Organization = optional customer entity (org_). Membership = node-to-venture connection. Every node has its own mesh of nodes. Fractal.","tables":{"onamerica_nodes":"Sovereign identity — id, mhs_id, email, name, phone. Each node is a point of presence on the mesh.","onamerica_ventures":"Platform registry — id (ven_), code, name, industry_id, domain, config_json","onamerica_organizations":"Optional customer entities within ventures — id (org_), venture_id, code, name, legal_type","onamerica_industries":"Hierarchical taxonomy — id, code, name, parent_industry_id (self-referential)","onamerica_memberships":"Node-to-venture connections — node_id, venture_id, org_id (optional), role, tier, verification_status","onamerica_sessions":"Device + venture sessions — node_id, venture_id, auth_method, jwt_jti","onamerica_consents":"Consent records — 13 types, GDPR Art. 7, inline in auth responses","onamerica_ops_log":"Universal audit trail — every auth event, every venture","magic_tokens":"Ephemeral 8-digit consent codes — SHA-256 hashed, venture-scoped, 15-min TTL, brute-force protected"}},"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."}},"endpoints":[{"method":"POST","path":"/api/auth/magic-link","description":"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","body":"{ email, venture?, name? }","response":"{ sent, token }","auth":false},{"method":"POST","path":"/api/auth/magic-code","description":"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)","body":"{ signin, venture? } | { email, code, venture? } | { code, venture? } (deprecated)","response":"{ consumed, node, player, session }; every failure 401 { consumed: false, error }; the throttle 429 { consumed: false, error }","auth":false},{"method":"POST","path":"/api/auth/invite","description":"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)","body":"{ email, name?, role?=member, venture?, send_email?=true }","response":"{ ok, invited, mhs_id, role, email_sent, email_error }","auth":true},{"method":"GET","path":"/api/auth/token-status/:token","description":"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","response":"{ consumed, node: { id, mhsId, name, email, role, pending_role, verification_status, tenants[], consents{} } }","auth":false},{"method":"GET","path":"/api/auth/verify-token/:token","description":"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","response":"HTML auto-close page","auth":false},{"method":"GET","path":"/api/auth/role/:email","description":"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","response":"{ email, role, tier, verified, pending_role, pending_tier, verification_status, tenants[] }","auth":"service-binding"},{"method":"GET","path":"/api/auth/staff","description":"List staff for venture. Caller must hold a live session and be a verified admin or sysadmin of that venture.","response":"{ staff[] }","auth":true,"note":"Venture resolved from Origin/X-Venture header"},{"method":"GET","path":"/api/auth/mhs-id/:email","description":"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).","response":"{ mhs_id, seq, name, email }","auth":true},{"method":"PUT","path":"/api/auth/mhs-id/suffix","description":"Self-service MHS ID suffix change","body":"{ email, suffix }","response":"{ mhs_id }","auth":true},{"method":"POST","path":"/api/auth/logout","description":"Revoke session — invalidate fleet token server-side","body":"{ session_token?, email?, venture? }","response":"{ ok, revoked }","auth":false},{"method":"POST","path":"/api/auth/mint-internal","description":"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)","body":"{ service, aud, scope, ttl_seconds<=300 }","response":"{ token, token_type, expires_in, aud, scope }","auth":"service-binding"},{"method":"GET","path":"/api/verify/:id/confirm","description":"Trust chain: confirm page — no side effect. Query carries by, exp, t (the signed capability minted into the admin link).","auth":"signed-link-capability"},{"method":"POST","path":"/api/verify/:id/confirm","description":"Trust chain: confirm — performs the change. Body carries by, exp, t (the signed capability from the link).","body":"{ by, exp, t }","auth":"signed-link-capability"},{"method":"GET","path":"/api/verify/:id/followup","description":"Trust chain: follow up page — no side effect. Query carries by, exp, t (the signed capability minted into the admin link).","auth":"signed-link-capability"},{"method":"POST","path":"/api/verify/:id/followup","description":"Trust chain: follow up — performs the change. Body carries by, exp, t (the signed capability from the link).","body":"{ by, exp, t }","auth":"signed-link-capability"},{"method":"GET","path":"/api/verify/:id/deny","description":"Trust chain: deny page — no side effect. Query carries by, exp, t (the signed capability minted into the admin link).","auth":"signed-link-capability"},{"method":"POST","path":"/api/verify/:id/deny","description":"Trust chain: deny — performs the change. Body carries by, exp, t (the signed capability from the link).","body":"{ by, exp, t }","auth":"signed-link-capability"},{"method":"GET","path":"/api/verifications/pending","description":"List pending verifications. Caller must hold a live session and be a fleet operator (a verified sysadmin of venture mhs).","auth":true},{"method":"GET","path":"/.well-known/fleet-auth.json","description":"This spec (machine-readable)","auth":false}],"venture_context":{"resolution_order":["1. Explicit \"venture\" field in request body","2. X-Venture header","3. Origin header domain matching (golflink.onamerica.org → golflink)","4. Default: golflink"],"registered_ventures":[{"code":"link","domain":"link.onamerica.org","customer":"Vets Whole In One","product_domain":"link.onamerica.org","industry":"veterans"},{"code":"weyland","domain":"weyland.onamerica.org","customer":null,"product_domain":"weyland.onamerica.org","industry":"doors"},{"code":"mhs","domain":"helmcorp.cc","customer":null,"product_domain":"helmcorp.cc","industry":"technology"},{"code":"iaas","domain":"iaas.onamerica.org","customer":null,"product_domain":"iaas.onamerica.org","industry":null},{"code":"argo","domain":"deck.onamerica.org","customer":"Argo Consulting LLC","product_domain":"deck.onamerica.org","industry":null},{"code":"pad","domain":"weyland.onamerica.org","customer":"Precision Auto Doors LLC","product_domain":"weyland.onamerica.org","industry":"doors"}],"industries":[{"code":"construction","name":"Construction","parent":null},{"code":"doors","name":"Door Hardware","parent":"ind_construction"},{"code":"electrical","name":"Electrical","parent":"ind_construction"},{"code":"mechanical","name":"Mechanical","parent":"ind_construction"},{"code":"nonprofit","name":"Nonprofit Organizations","parent":null},{"code":"technology","name":"Technology","parent":null},{"code":"veterans","name":"Veteran Services","parent":"ind_nonprofit"}]},"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"}},"fleet_services":"DYNAMIC — loaded from D1 onamerica_services at runtime. 11 services across 2 substrates. Query sensorium-onamerica GET /api/services for live registry.","sensorium":{"url":"https://sensorium-onamerica.ron-helms.workers.dev","role":"Fleet nervous system — unified perception, health probing, FTBs, investigations","health":"GET /api/health — probes all services from D1 registry with primary → fallback failover","auth_nerve":"GET /api/nerve/auth — deep authentication measurement (17 metrics)","services":"GET /api/services — the wiring harness (D1 service registry)","digest":"GET /api/digest — complete system awareness snapshot"}}