KDCube
← Engineering
KDCube Engineering · Concept

Connection Hub Storage: Tokens, Edges, and the Line Between Them

No magic token table: handles, secrets, metadata, edges, and sessions each live where their lifecycle fits. The storage model is the design.

16 July 2026Engineering11 minConceptPunch Card
Connection Hubtoken storageidentity edgesdelegated credentialsConnectionStoredelegated cardsactive catalogRedis projection

Connection Hub exists because integration authority arrives from several directions. A browser user signs in through platform authority. An external tool such as Claude Code asks for delegated access to a KDCube resource. A KDCube app connects to a user's Gmail, Slack, or custom OAuth account. A Telegram Mini App proves a channel actor and then asks the platform user to attach that actor to the platform account.

Those journeys should not share one storage bucket. The records look related because all of them answer "who may act?", but they answer it at different boundaries.

THE FILING RULE

Different authority facts live in different places because they have different lifecycles: delegated access is a durable, versioned card behind an opaque handle; connected provider accounts split metadata from raw credentials; a Telegram link is an identity edge; deployment secrets remain deployment secrets. The storage model is the design.

the.archive.mapMAP
descriptors + secrets          authority/provider policy   → deployment plane
durable card + catalog history current pointers, revisions   → delegated-by-KDCube
Redis serving views            sessions + projections     → per-call resolution
ConnectionStore metadata       connected provider          → delegated-to
                               accounts                      provider account
Connection edge store          linked identities           → identity projection
                               and challenges                telegram → platform

This article is the storage map. It complements the broader Connection Hub architecture article by showing where the bytes and authority records live, why that split exists, and which record to inspect when a link, token, or delegated client does not behave as expected.

One terminology note: current routes and code still use bundle_id, bundles.yaml, and /api/integrations/bundles/.... In product prose, the deployable unit is an app. Literal identifiers keep their historical names.

01 The four records people confuse

The quickest way to debug Connection Hub is to name the record before looking for it.

QuestionRecordStorage owner
"Claude Code got an OAuth token. Where is it?"Exact bearer session plus a selected delegated cardSession authority selects access_id; Connection Hub bundle storage owns immutable card revisions and current.json
"I created a manual Delegated by KDCube token. Where is it?"Exact bearer session plus a selected delegated cardThe raw bearer is displayed once; the durable card and revision history live in Connection Hub bundle storage
"A user connected Gmail or another provider. Where are those provider tokens?"Connected account metadata plus raw provider token secretConnectionStore metadata + user-scoped secrets
"A Telegram user linked to a platform user. Where is that link?"Connection Hub identity edgeConnection edge store; JSON-backed today

The first two are inbound delegated access. They let an external actor call KDCube. The third is outbound provider access. It lets KDCube call another provider for a user. The fourth is neither a token nor a provider account. It is a relationship between two independently proven identities.

02 Delegated by KDCube: the token is a handle

For delegated OAuth, an external client discovers a protected KDCube resource, registers or identifies itself, sends the user through the authorization flow, and exchanges a code for a bearer. Claude Code follows this shape when it uses Connection Hub as an OAuth authorization server for a protected MCP surface.

delegated.oauthJOURNEY
external MCP client
  → protected resource metadata
  → authorize user and collect consent
  → token endpoint returns bearer handle
  → later request: Authorization: Bearer <opaque handle>
  → managed guard hashes handle
  → session selects one exact access_id
  → loads the current durable card and active catalog
  → intersects them for this invocation
  → checks resource, tool, grants, expiry, and revocation
  → projects delegate actor + approving platform user

The bearer string is not the authority model. It is a handle into server-side state. The server-side record carries the resource grants, selected operations or tools, grantor identity, delegate identity, identity scope, expiry, and revocation state.

handle.vs.recordSPLIT
Bearer token bytes
  displayed to client
  sent over Authorization header
  never treated as the source of policy

Server-side grant/session
  selected resources
  selected tools/operations
  resource grants
  grantor platform user
  delegate actor
  expiry + revocation

Manual "Delegated by KDCube" access uses the same principle. The UI may show the generated token once because the user must copy it to an external script or tool. After that, listings expose metadata such as label, status, expiry, and last-four style hints. They do not need to expose raw bearer bytes again. The runtime validates the presented bearer, uses its session binding to select one exact access_id, and resolves that card's current durable revision. The card is intersected with the current active delegated catalog for each invocation. The session selects authority; it never freezes it.

DELEGATED AUTHORITY: SELECT, RESOLVE, INTERSECT OPAQUE BEARER session binding access_id selector, not policy CONNECTION HUB validate actor binding load exact current card load current active catalog DURABLE CARD immutable revisions current.json nonsecret authority ACTIVE CATALOG immutable versions active.json operator ceiling THIS CALL card ∩ catalog effective authority REDIS SERVING PROJECTION rebuilt from current documents EDIT, REVOKE, OR CATALOG CHANGE APPLIES TO THE NEXT INVOCATION
Fig. 1 — the bearer selects one card; current card and catalog state decide each call.

03 Connected provider accounts: metadata is not credential material

When KDCube is delegated to a provider account, the storage is different. The user is not giving an external client a KDCube bearer. The user is giving KDCube a provider credential so an app, tool, or named service can call that provider.

ConnectionStore owns the metadata surface:

connection.storePATHS
<app_storage_root>/connections/<safe_user_id>/accounts.json
<app_storage_root>/connections/_oauth_states/<sha256(state)>.json

That metadata can contain provider id, account id, display name, provider subject, approved claims, credential health, and has_token. It is not the raw provider token store.

An OAuth reapproval for an existing account carries two additional pieces of short-lived state: the existing account_id and claims_mode: replace. They make the callback update the same account and store exactly the checked claim set, including removals, instead of silently unioning old and new claims. A new connection or an incremental demand may still use additive claims. The durable account record stores only the resulting claims; the OAuth state that authorized the transition remains short-lived and single-use.

Raw provider access/refresh tokens, app passwords, and equivalent credential bytes live in user-scoped secrets. By default, Connection Hub stores them in shared user scope so another app acting for the same platform user can resolve the account through the broker without copying token bytes into its own app storage.

two.drawersSPLIT
connected account metadata
  accounts.json
  provider, account_id, label, claims, health, has_token

provider credential bytes
  user-scoped secret
  connections.accounts.<safe_account_id>.tokens

This split lets the UI say "Gmail is connected and has gmail:send" without placing Gmail tokens in the UI payload, the app descriptor, the agent prompt, or the connected-account metadata file.

Conversation file references follow the same handle rule. A conv:fi: string names an artifact under its owner, tenant, project, conversation, and turn; it does not grant cross-user access by itself. When an account-backed action needs that file, the provider side materializes the bytes through the artifact service under the current actor and grant authority, then streams them to the backing provider. The temporary workspace path produced for an agent is disposable process state, not a record in Connection Hub and not portable authorization.

CONNECTED ACCOUNT · METADATA DRAWER, SECRET DRAWERCONNECTIONSTORE · accounts.jsonprovider · account_id · labelprovider subject · approved claimscredential health · has_tokenSAYS A TOKEN EXISTS · HOLDS NO TOKENUSER-SCOPED SECRETconnections.accounts. <safe_account_id>.tokensaccess / refresh token bytesserver-side resolvertrusted SDK · broker onlyUI: “Gmail connected · gmail:send”your app · tool · named servicecalls the provider through the broker,never holding the bytesNO BROWSER / MODEL PATHTO THE SECRET DRAWERMETADATA FOR THE UI · BYTES FOR THE BROKER · NEVER THE SAME DRAWER
Fig. 2 — the metadata drawer may say a token exists; only the secret drawer holds its bytes.

04 Telegram linking is an edge, not a session

Telegram linking is the record most easily mistaken for authentication state. The Telegram Mini App proves a Telegram actor with initData; the browser proves the platform user with the normal KDCube session; Connection Hub writes an edge only after the user explicitly approves the relationship and selected grants.

link.journeyJOURNEY
Telegram Mini App
  proves telegram:<telegram_user_id>
        ↓
pending provider-claim challenge
        ↓
KDCube browser session
  proves platform:<platform_user_id>
        ↓
user selects grants and confirms
        ↓
connection edge:
  telegram.kdcube_ref:<telegram_user_id>platform:<platform_user_id>

Current implementation stores the temporary challenge and final edge in the Connection Hub app's storage:

edge.pathsPATHS
<connection-hub app storage>/connections/connection-edge-challenges.json
<connection-hub app storage>/connections/connection-edges.json

The final edge is conceptually:

connection_hub.edge.v1JSON
{{
  "schema": "connection_hub.edge.v1",
  "relationship": "delegates_to",
  "from": {{
    "authority_id": "telegram.kdcube_ref",
    "provider": "telegram",
    "subject": "100200300",
    "user_id": "telegram_100200300"
  }},
  "to": {{
    "authority_id": "platform",
    "provider": "platform",
    "subject": "02e53484-...",
    "user_id": "02e53484-..."
  }},
  "grants": [
    "identity:family",
    "economics:platform-user"
  ],
  "proof": {{ "challenge_id": "..." }},
  "metadata": {{
    "source": "telegram_miniapp",
    "telegram": {{ "id": "100200300", "username": "..." }}
  }}
}}

The edge does not make Telegram become the platform browser session. The actor for Telegram-originated work remains Telegram. The edge gives a platform boundary a way to ask: "does this Telegram actor have an approved projection to this platform subject, and which grants may cross?" That is why the grants matter:

GrantWhat it enables
identity:familyIdentity-family reads can include runtime identities connected through this edge.
economics:platform-userEconomics can evaluate spend against the linked platform user.
kdcube:role:* or permission grantsA boundary can project only roles or permissions explicitly allowed by the edge and currently held by the platform identity.

Without the edge, Telegram is still externally authenticated by Telegram proof, but it is not a platform user. Without the selected grant, the edge may exist but not satisfy the boundary that wants platform authority, economics authority, or a specific role projection.

TELEGRAM LINK · AN EDGE, NOT A SESSIONTelegram actorproven by initDatatelegram:100200300platform userproven by the browser sessionplatform:02e53484-…the connection edgedelegates_to · approved by the usergrants: identity:family · economics:platform-userPENDINGconnection-edge-challenges.jsonCOMPLETEconnection-edges.jsonschema: connection_hub.edge.v1actor · provenance stay Telegramprojection crosses by grant onlyNO EDGE → NO PLATFORM PROJECTION · NO GRANT → THAT BOUNDARY STAYS CLOSED
Fig. 3 — the edge is a filed relationship; only its selected grants cross.

05 One storage surface per lifecycle

The storage split gives each revocation and refresh operation a precise target.

RecordCurrent storageSensitive bytes?Typical revoke/repair
Authority provider configApp descriptors and platform configurationSecret refs onlyUpdate descriptor/config; rotate referenced secret separately
Deployment OAuth/client secretsPlatform secrets lifecycleYesRotate platform secret
OAuth code and consent-CSRF transition stateRedis one-time recordsYes, short-lived auth stateConsume atomically or expire; fail closed when unavailable
Delegated card revisions and current pointerConnection Hub bundle storage under delegated-cards/v1/...Complete nonsecret authority; no reusable bearer/provider secretsPublish an immutable revision, then replace current.json
Delegated catalog versions and active pointerConnection Hub bundle storage under delegated-catalog/v1/...Nonsecret operator policyPublish a complete immutable version, then replace active.json
Delegated card/catalog serving viewRedis projections rebuilt from validated durable current documentsNo raw bearer/provider secretEvict or rebuild; a miss is not authority loss
Delegated bearer sessionRedis app-session authority with exact access_id bindingYes, session stateInvalidate session or let TTL expire
Browser operation-CSRF proofShared request-proof storeYes, short-lived request stateConsume once for the exact subject/app/operation; fail closed if unavailable
Manual delegated tokenOpaque handle plus exact bearer-session/card bindingRaw handle shown onceRevoke the card; the next invocation is denied
Per-account agent binding (account_scope)Field in the durable delegated card revisionNo raw provider tokenEdit the caller's card (Delegated by KDCube)
Connected account metadataConnectionStore account JSONNo raw provider tokenDisconnect account or change approved claims
Connected account provider tokenUser-scoped secretsYesDelete/rotate user secret; reconnect provider
Telegram/platform link challengeConnection edge challenge storeNo provider tokenExpire or complete challenge
Telegram/platform linkConnection edge storeNo provider tokenRemove edge

The records that authorize one-time OAuth transitions are consumed atomically. Authorization-code exchange and consent-CSRF validation use async Redis Lua compare-and-consume operations, so two workers cannot both accept the same record. Refresh rotation first checks current live authority, then atomically replaces the matching old refresh record with one successor. Browser operation-CSRF is a separate request-proof lifecycle: its token is bound to the authenticated subject and exact app operation and is also consumed once.

The same state boundary protects claim replacement. account_id and claims_mode travel inside the signed OAuth state, not in a trusted browser field at callback time, so a reapproval cannot be redirected to another account or silently changed from replacement to union.

Pointer-backed delegated credentials keep the card as live authority after issuance. Each managed REST request, each MCP tool call (including each item in a batch), and each native named-service invocation resolves the selected card and current active catalog again. Effective permission is their intersection. Narrowing, revocation, or catalog removal therefore applies to the next invocation; an invocation already admitted is allowed to finish.

Redis accelerates this resolution but does not own the card or catalog. A cache miss can reconstruct the projection from validated durable current documents. An unreadable current catalog is a logged 503 temporarily_unavailable; an operation retained by the card but removed from the active catalog is a 403 delegated_capability_no_longer_available; an operation absent from the card is the ordinary missing-grant denial. The runtime never substitutes an older token snapshot or deployment default.

For named services, the managed surface guard admits the outer resource and tool. The common dispatcher decodes namespace and operation and runs explicit NamedServiceAdmission before provider selection. A relay carries the actor and a typed, nonsecret card selector; its target validates the selector against the restored actor and performs the fresh Connection Hub lookup there. Account scope is bound only for the admitted provider invocation and restored afterward. Streaming is admitted once per invocation, not once per chunk.

This is intentionally not "everything in Postgres" yet. The current reference implementation keeps connection edges JSON-backed behind the ConnectionEdgeStore contract. The important contract is the edge shape and API semantics, not the file. A production storage backend can move the edge and challenge records to a database without changing what an app asks Connection Hub to resolve.

06 What to inspect when something breaks

Debugging is faster when the failure is mapped to the right record.

triage.cardsINSPECTION
OAuth client receives bearer, but MCP call is denied
  inspect delegated grant/session:
    resource match · selected tool or operation · grant list · expiry/revocation

Connected Gmail tool says consent is missing
  inspect connected account metadata:
    account exists · approved claims cover the tool · has_token and health
  then inspect user-scoped secret only from server-side tooling

Connected account holds the claim, but the agent still cannot use it
  symptom: agent_grant_required
  the account is connected and holds the claim, but this agent's grant is not bound to use it there
  fix on the agent's grant card (Delegated by KDCube), not by reconnecting the provider

Telegram Mini App says linked=false
  inspect connection edge:
    from provider=telegram · from subject=<telegram_user_id>
    to authority=platform · status active/linked

Telegram automation runs as registered but not privileged
  inspect selected edge grants and the current platform roles
  the edge cannot project roles the platform identity does not hold
  and cannot project roles not allowed by the edge

The pattern is consistent: the proof that entered the system is not the authority by itself. The runtime validates proof, resolves the relevant Connection Hub record, intersects that record with current platform/provider state, and carries the projection only to the boundary that asked for it.

07 Why the boundary matters

Putting every credential-like object into one table would make the system harder to reason about: a delegated bearer would look like a provider account token; a Telegram identity edge would look like a platform login session; a provider refresh token would look like a resource grant; a copied secret would become the accidental source of authority.

ONE DRAWER EACH

Connection Hub separates handles, secrets, metadata, edges, and sessions. Each surface has one owner, one revocation path, and one boundary where it is allowed to project authority.

The result is boring in the best engineering sense. A bearer token is a handle. A connected account is metadata plus a user secret. A Telegram link is an edge. A platform session is platform authority. Product code can then ask Connection Hub for the projection it needs instead of becoming a private credential store.

Current implementation paths to inspect across the portable policy and app source trees: the Connection Hub edge store and the Connection Hub app entrypoint.

· Related articles

· Documentation on GitHub

KDCube Engineering
№ 2026-07-16 · kdcube.tech