You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement the optional trust layer that upgrades A2H interactions from lightweight to auditable mode: JWS signing, strong authentication, audit logging, and replay protection.
Tasks
JWS Signing
Sign A2H intents: bind intent → context → timestamp into a JWS
AuthChallengeManager: issue + verify challenges for webauthn, totp, sms_otp methods
Challenges scoped to a formKey, expire after configurable TTL (default: 5 min)
getVerifiedChallenge(): lets the form submission route confirm auth completed
TrustLayerManager (src/trust-layer.ts)
Single entry point that wires all subsystems together
TrustLayerManager.create(): async factory, auto-generates keys when not provided
enabled: false → all methods throw immediately — zero overhead for lightweight mode
signIntent() + signResponse(): full evidence chain signing with audit logging
checkReplay(): validates timestamp + nonce, records nonce on success
issueAuthChallenge() / verifyAuthChallenge(): full lifecycle with audit logging
queryAuditLog(): filter by turnId, threadId, eventType, date range
Server-side integration
Audit API (src/app/api/audit/route.ts)
GET /api/audit — query audit log with filters: turnId, threadId, channelId, eventType, fromDate, toDate, limit, offset
Returns 404 when TRUST_LAYER_ENABLED is not set (zero overhead for lightweight mode)
Form Auth Endpoint (src/app/api/form/[formKey]/auth/route.ts)
POST /api/form/:key/auth — issue an auth challenge (returns challengeId, method, challenge, expiresAt)
PUT /api/form/:key/auth — verify challenge response (TOTP code or WebAuthn assertion fields)
Returns challengeId on success; required for form submission when trust is active
Form Route Updates (src/app/api/form/[formKey]/route.ts)
GET: now returns requiresAuth: true + authMethods when trust is active; emits intent_rendered audit event on first view
POST: rejects without verified challengeId when trust active (401); validates challenge is for correct form (prevents cross-form replay); emits response_received audit events
Trust Service (src/lib/trust-service.ts)
Server-wide singleton attached to globalThis (survives Next.js hot-reload)
MongoAuditStorage: writes audit entries to audit_log MongoDB collection
Config from env: TRUST_LAYER_ENABLED, TRUST_LAYER_ALGORITHM, TRUST_LAYER_TIMESTAMP_TOLERANCE, TRUST_LAYER_NONCE_TTL, WEBAUTHN_RP_ID, TRUST_DEFAULT_AUTH_METHOD
DB Updates (src/lib/db.ts)
saveAuditEntry() / queryAuditLog() helpers for the audit_log collection
ensureIndexes() extended with audit_log indexes (id unique, turnId+timestamp, eventType+timestamp, timestamp TTL-friendly)
Activity
claude commentedon Apr 10, 2026
@claude Implement this issue.
meta/17meta/17fixes #14in the PR bodyclaude commentedon Apr 10, 2026
Claude finished @claude[bot]'s task in 13m 59s —— View job •
claude/17-issue-14-20260410-2337• Create PR ➔Implementing Trust Layer — JWS, Auth, Audit
Tasks:
packages/trustpackage — types, JWS, replay, audit, authWhat was implemented
packages/trust— the trust layer libraryJWS Signing (
src/jws/index.ts)signIntent()— binds A2H intent → context → timestamp → turnId into a JWS compact serializationsignResponse()— binds human response → intent nonce (cryptographic chain: intent → response)generateKeyPair(),importPublicKey(),exportPrivateKey()for key managementReplay Protection (
src/replay/index.ts)ReplayGuard: in-memory nonce store with configurable TTL (default: 1h)validateTimestamp(): rejects stale (intent_expired) and future-dated (intent_future) intentscheckNonce(): prevents nonce reuse (nonce_reused) — covers idempotency keys + JTI claimsprune(): lazy eviction of expired noncesAudit Logging (
src/audit/)AuditLogger: structured entries covering the full A2H decision path:intent_sent → intent_rendered → auth_challenge_issued → auth_challenge_completed → response_received → evidence_signedInMemoryAuditStorage: in-process storage (cap: 10k entries), suitable for devAuditStorageAdapterinterface: pluggable — MongoDB impl wired in the serverStrong Authentication (
src/auth/)encodeBase32/decodeBase32helperswebauthn,totp,sms_otpmethodsformKey, expire after configurable TTL (default: 5 min)getVerifiedChallenge(): lets the form submission route confirm auth completedTrustLayerManager (
src/trust-layer.ts)TrustLayerManager.create(): async factory, auto-generates keys when not providedenabled: false→ all methods throw immediately — zero overhead for lightweight modesignIntent()+signResponse(): full evidence chain signing with audit loggingcheckReplay(): validates timestamp + nonce, records nonce on successissueAuthChallenge()/verifyAuthChallenge(): full lifecycle with audit loggingqueryAuditLog(): filter by turnId, threadId, eventType, date rangeServer-side integration
Audit API (
src/app/api/audit/route.ts)GET /api/audit— query audit log with filters:turnId,threadId,channelId,eventType,fromDate,toDate,limit,offsetTRUST_LAYER_ENABLEDis not set (zero overhead for lightweight mode)Form Auth Endpoint (
src/app/api/form/[formKey]/auth/route.ts)POST /api/form/:key/auth— issue an auth challenge (returnschallengeId,method,challenge,expiresAt)PUT /api/form/:key/auth— verify challenge response (TOTP code or WebAuthn assertion fields)challengeIdon success; required for form submission when trust is activeForm Route Updates (
src/app/api/form/[formKey]/route.ts)GET: now returnsrequiresAuth: true+authMethodswhen trust is active; emitsintent_renderedaudit event on first viewPOST: rejects without verifiedchallengeIdwhen trust active (401); validates challenge is for correct form (prevents cross-form replay); emitsresponse_receivedaudit eventsTrust Service (
src/lib/trust-service.ts)globalThis(survives Next.js hot-reload)MongoAuditStorage: writes audit entries toaudit_logMongoDB collectionTRUST_LAYER_ENABLED,TRUST_LAYER_ALGORITHM,TRUST_LAYER_TIMESTAMP_TOLERANCE,TRUST_LAYER_NONCE_TTL,WEBAUTHN_RP_ID,TRUST_DEFAULT_AUTH_METHODDB Updates (
src/lib/db.ts)saveAuditEntry()/queryAuditLog()helpers for theaudit_logcollectionensureIndexes()extended with audit_log indexes (id unique, turnId+timestamp, eventType+timestamp, timestamp TTL-friendly)|