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
Every consumer app (gtm-agent, creative-agent, legal-agent) hand-rolls the same Tangle-platform integration glue: the cross-site SSO login flow, the integrations-hub proxy routes, and the billing HTTP/guard layer. agent-app should own this protocol behind typed seams — apps should configure it, not reimplement it.
Problem (audit findings)
A cross-repo audit of the three consumer apps found:
~1,100 lines of platform glue in gtm-agent alone, of which roughly three-quarters is generic protocol any Tangle app needs verbatim (src/routes/auth.tangle.{start,callback}.ts, src/lib/.server/billing/platform.ts, src/lib/.server/auth-utils.ts, 5 near-identical api.integrations.hub.* proxy routes).
Three different answers to the same four problems:
SSO state signing: gtm = HMAC without TTL (node:crypto), legal = HMAC + timestamp + TTL (crypto.subtle), creative = unsigned random nonce with exact-match only.
Execution-key resolution: only gtm adopts resolveUserTangleExecutionKeyForUser from agent-app/runtime; creative re-implements it standalone; legal doesn't wire it at all.
Session creation / exchange error handling: near-identical copy-paste blocks in all three callbacks.
The package already proves the right pattern elsewhere (WorkspaceKeyStore/KeyCrypto seams + preset-cloudflare reference impls); the SSO/hub/billing area just never got the same treatment.
Proposed approach
One new subpath export @tangle-network/agent-app/platform with four modules, structural seams only (no imports of agent-runtime, better-auth, drizzle, or consumer code — same rule as BrokerTokenMinter in src/tangle/index.ts):
platform/sso.ts — hardened signed state (WebCrypto HMAC-SHA256, TTL in the signed payload, constant-time compare) + createTangleSsoHandlers start/callback orchestration; TangleSsoAccountStore seam covers both the link-table and session-column storage styles.
platform/hub.ts — createHubProxyRoutes factory with the existing error contract preserved verbatim (412 tangle_link_required, hub-error status/code passthrough); structural error guards so bundler module duplication can't break instanceof.
platform/billing.ts — fetch-backed platform billing transport (user-bearer reads, service-token deduct), tier policy/state with fail-closed null-key handling, and a concrete implementation of /billing's PlatformBillingClient seam (type-only import).
Plus cookie helpers (serializeCookie / clearCookieHeader / readCookieValue) in /web.
Acceptance criteria
./platform subpath ships the four modules above with vitest coverage (state round-trip/tamper/expiry, all handler error redirects, proxy route shapes + error mapping, billing header/body contracts, guard paths)
gtm-agent adopts it end-to-end with its existing behavior byte-compatible (cookie names, error codes, 412/402 semantics, tangle_link schema) — measured net deletion of its hand-rolled glue
No new runtime deps; agent-runtime stays an optional peer; ./platform not re-exported from the root barrel (collides with /billing's Platform* names)
Version bumped (0.5.0, additive) — publish via v0.5.0 tag after merge
gtm-agent adoption verified on a local branch (feat/platform-module-adoption): −453 net lines, 783 tests green, live smoke-tested against id.tangle.tools
Follow-ups (separate issues/PRs): adopt /platform in creative-agent (replaces its unsigned SSO state) and legal-agent (also needs its LLM path wired to the session-bound key)
Summary
Every consumer app (gtm-agent, creative-agent, legal-agent) hand-rolls the same Tangle-platform integration glue: the cross-site SSO login flow, the integrations-hub proxy routes, and the billing HTTP/guard layer. agent-app should own this protocol behind typed seams — apps should configure it, not reimplement it.
Problem (audit findings)
A cross-repo audit of the three consumer apps found:
src/routes/auth.tangle.{start,callback}.ts,src/lib/.server/billing/platform.ts,src/lib/.server/auth-utils.ts, 5 near-identicalapi.integrations.hub.*proxy routes).node:crypto), legal = HMAC + timestamp + TTL (crypto.subtle), creative = unsigned random nonce with exact-match only.tangle_linktable; legal/creative =sessions.tangleApiKeycolumn.resolveUserTangleExecutionKeyForUserfromagent-app/runtime; creative re-implements it standalone; legal doesn't wire it at all.The package already proves the right pattern elsewhere (
WorkspaceKeyStore/KeyCryptoseams +preset-cloudflarereference impls); the SSO/hub/billing area just never got the same treatment.Proposed approach
One new subpath export
@tangle-network/agent-app/platformwith four modules, structural seams only (no imports of agent-runtime, better-auth, drizzle, or consumer code — same rule asBrokerTokenMinterinsrc/tangle/index.ts):platform/sso.ts— hardened signed state (WebCrypto HMAC-SHA256, TTL in the signed payload, constant-time compare) +createTangleSsoHandlersstart/callback orchestration;TangleSsoAccountStoreseam covers both the link-table and session-column storage styles.platform/hub.ts—createHubProxyRoutesfactory with the existing error contract preserved verbatim (412tangle_link_required, hub-error status/code passthrough); structural error guards so bundler module duplication can't breakinstanceof.platform/billing.ts— fetch-backed platform billing transport (user-bearer reads, service-token deduct), tier policy/state with fail-closed null-key handling, and a concrete implementation of/billing'sPlatformBillingClientseam (type-only import).platform/guards.ts— auth guard (302 page / 401 JSON), admin allowlist guard (404),assertBillableBalance(402billing.balance_required) wired toisTangleBillingEnforcementDisabled.Plus cookie helpers (
serializeCookie/clearCookieHeader/readCookieValue) in/web.Acceptance criteria
./platformsubpath ships the four modules above with vitest coverage (state round-trip/tamper/expiry, all handler error redirects, proxy route shapes + error mapping, billing header/body contracts, guard paths)tangle_linkschema) — measured net deletion of its hand-rolled glue./platformnot re-exported from the root barrel (collides with/billing'sPlatform*names)v0.5.0tag after mergeContext
feat/platform-glue)feat/platform-module-adoption): −453 net lines, 783 tests green, live smoke-tested against id.tangle.tools/platformin creative-agent (replaces its unsigned SSO state) and legal-agent (also needs its LLM path wired to the session-bound key)