KDCube
← Engineering

A user can now create a bounded credential for their own automation. That sounds small, but it closes a gap: KDCube could let an external app negotiate access through OAuth/MCP, and let a hosted application protect a managed surface — but a user also needs a simpler path: "give this script, agent, CI job, or DevOps task a token that represents me, only for the resources and operations I choose."

That is Delegated Access in Connection Hub — the Delegated by KDCube tab.

signed-in KDCube user
        |
        | creates delegated access in Connection Hub
        v
short-lived bearer token for automation
        |
        | Authorization: Bearer ...
        v
managed REST / MCP / platform surface
        |
        | bearer selects access_id
        | guard resolves current card against active catalog
        v
route receives grantor context and delegated-actor provenance

The token is not a password and not a full login session. It is a delegated credential: revocable, scoped, tied to the approving platform user, and checked against current durable authority on every invocation.

The whole idea in one line

Connection Hub lets a signed-in user mint a short-lived bearer token for automation. The authenticated session selects one delegated card by exact access_id. That card stores resource_grants: resource → grants[], plus selected operations; the active catalog supplies the deployment ceiling. We do not store separate resources and grants lists — that would let the meaning drift. We store one shape:

{
  "resource_grants": {
    "https://runtime.example/A": ["records:read"],
    "https://runtime.example/B": ["records:write"]
  },
  "operations": ["records_search", "records_export"]
}

For a request to A, the guard uses grants from the matching A entry. It does not take a global union of every grant on the token. A token with {A: read, B: write} cannot use write on A.

What this is, and what it is not

Delegated Access is a KDCube-issued credential for entering KDCube. It is not the same as a user connecting their Gmail, Slack, iCloud, or LinkedIn account. Those are user-connected provider integrations — KDCube stores provider credentials so an application can call an external service for the user. Delegated Access points the other way: the user gives an automation a bounded way to call KDCube.

user-connected provider account
  KDCube -> external provider API
  example: KDCube reads the user's Gmail

delegated automation access
  automation -> KDCube API
  example: a script publishes news, exports conversations, or calls an app API

It is also different from OAuth/MCP connector setup such as Claude. Claude can negotiate with KDCube as a client and receive a delegated credential after a consent flow. Delegated Access is the manual, user-facing version: the user opens Connection Hub and creates a token directly.

The user flow

USER-CREATED DELEGATED ACCESS User signed-in grants + operations Connection Hub Delegated Access commits current card Automation script · agent · CI Bearer kst1… managed surface REST · MCP · platform checks card ∩ catalog create token (once) Bearer revoke anytime CURRENT CARD ∩ ACTIVE CATALOG durable authority · resolved once per invocation news publish → [news:write] named services → mem:[object.search] The bearer proves identity and selects access_id; it does not freeze authorization. Product context derives from the grantor; the automation remains the delegated actor for provenance.
The bearer is an identity proof and exact card selector. The next invocation resolves the current card against the active catalog.

The UI should feel like selecting surfaces, not assembling OAuth internals:

Create automation access

Name:  nightly news publisher

Resources:
  [x] News operations API
      [x] Publish news           news:write

  [x] KDCube conversations MCP
      [x] Read conversations     conversations:read

  [x] KDCube named services MCP
      [x] User memories
          [x] search             object.search · memories:read
          [ ] write              object.upsert · memories:write
      [x] Slack
          [x] search             object.search · slack:read
          [ ] act                object.action · slack:write
              provider prerequisite: Slack / slack:post

  [ ] All platform and application APIs
      admin-only                 kdcube:role:super-admin

After creation, the UI shows the raw token once (Authorization: Bearer kst1...). The Granted access list then shows metadata only: name, origin, approval/creation time, expiry, resource_grants, selected top-level operations, selected named-service operations, token last-four, and revoke.

Compatible top-level REST/MCP operations are derived server-side from the selected resource grants. For a named-services MCP resource, the user additionally selects exact namespace operations. If an operation needs a connected Slack, Gmail, or other provider claim, its prerequisite action leads to the existing Delegated to KDCube consent before the automation token is minted.

One registry for manual tokens, OAuth clients, and hosted agents

Granted access lists more than the tokens created in this form. When an external client (for example an MCP client such as Claude) connects through the KDCube OAuth consent flow, that approval registers in the same per-user registry:

manual token   badge: manual token    Created  <time>
OAuth connect  badge: connected app   Approved <time>
hosted agent   badge: agent           Granted  <time>

client id = kdcube-agent:<application>:<agent_id>

OAuth rows keep one row per (user, client, resource) — reconsent widens the same row and keeps the first-approval time. The label is the client's own registration name, so users see "Claude", not a dcr-... id. The row expires with the connection's refresh-token lifetime, so a connection that can no longer mint tokens disappears on its own.

Revoke takes effect at the next invocation for all three paths. A manual token's bound session is logged out. An OAuth connect loses its refresh token and its current delegated card becomes revoked. Work already admitted under one decision may finish; the next request or MCP tool call resolves the revoked state and fails.

A hosted-agent grant is deduplicated per user, agent client, and resource. Reconsent updates that row: a one-click approval merges its claims in, and an edit in the tab replaces the row's claim set exactly (unchecking narrows). The row extends the way it was born — an operation needing claims outside it raises a scoped consent demand naming exactly the missing grants. Granting one agent grants nothing to its siblings. Each turn the host reuses the bearer already bound to the row. The user's browser session never enters the agent.

The runtime flow

The automation does not become the browser user. It becomes a delegated client actor whose grantor is the signed-in platform user who approved the token.

delegated client
  external app | manual automation | hosted agent
  |  Authorization: Bearer kst1...
  v
KDCube request-auth surface
  |  authenticate token -> exact delegated_card_binding.access_id
  |  resolve current card + complete active catalog
  |  check resource, selected operation/tool, identity, and expiry
  |  project grantor as platform UserSession
  v
route handler / MCP tool / application operation
  |  sees platform user context + delegated provenance
  v
product code runs

The delegated client remains the actor. The route receives projected product/economics context, so application code can use the normal host-bound identity path rather than parsing tokens:

UserSession.user_id            -> grantor platform user id
UserSession.user_type          -> derived from projected grantor roles
UserSession.identity_authority -> delegated credential provenance:
                                  delegate actor, grantor, authority,
                                  resource_grants, operations, expiry

Product code should treat the request as delegated platform-user context. It should not parse the bearer token or infer the grantor itself. The managed boundary is a current-authority gate: a bearer without an exact delegated-card binding fails closed. The bearer proves identity and selects the card; it is not a frozen authorization snapshot.

The data model

The important persisted shape is resource_grants. The bearer token is only a handle. Its session retains the exact access_id; runtime guards use that selector to load the card's current durable revision and the complete active catalog for this invocation.

{
  "schema": "connection_hub.automation_access.v1",
  "access_id": "agent-...",
  "label": "research agent: news publisher",
  "client_id": "kdcube-agent:reporting@1-0:research",
  "grantor_subject": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "delegate_subject": "integration:kdcube-agent:reporting@1-0:research:a1b2c3d4-...",
  "source": "agent",
  "resource_grants": {
    "*/api/integrations/bundles/*/*/news@2026-05-20-12-05/operations/publish*": ["news:write"],
    "*/api/integrations/bundles/*/*/kdcube-services@1-0/public/mcp/conversations*": ["conversations:read"]
  },
  "operations": ["news_publish", "conversations_export"],
  "identity_scope": "grantor",
  "expires_at": 1783339200
}

This is one hosted-agent record. Manual tokens use source: manual with a generated automation:<id> client; OAuth connections use source: oauth. They reuse the schema and guard, not this record.

delegated-cards/v1/.../<access_id>/revisions/<immutable>.json
delegated-cards/v1/.../<access_id>/current.json

delegated-catalog/v1/versions/<immutable>.json
delegated-catalog/v1/active.json

Card revisions and catalog versions are immutable durable documents. Redis is their rebuildable serving projection. A Redis miss reads and validates the durable current documents before restoring that projection.

A generic named-services MCP resource can also persist the exact namespace-operation selection:

{
  "named_service_operations": {
    "*/kdcube-services@1-0/public/mcp/named_services*": {
      "mem": ["object.search"],
      "slack": ["object.search"]
    }
  }
}

Connection Hub validates these operation ids against the active delegated catalog and stores the user's exact selection in the current card. The managed MCP guard checks the outer resource and tool. The common dispatcher then applies explicit NamedServiceAdmission to the decoded namespace and operation. A native hosted-agent tool resolves the same authority in each _call; a relayed target validates typed selector and actor facts before resolving current state. Each MCP batch item is independent, while a provider byte stream is admitted once for its invocation. Provider-account claims remain separate Delegated to KDCube prerequisites; the provider token is never copied into the automation bearer.

  • Resource and grant cannot drift apart — grants live inside resource_grants[resource].
  • Revocation is current at the next invocation — each call resolves the current card and active catalog.
  • Catalog drift is visible and enforced — removed capabilities remain card history but are outside the active ceiling.
  • Wildcards are explicit resource entries — "*" is just another key in resource_grants.

Admin all-resource automation

Sometimes an administrator wants a token for DevOps automation: deploy, publish, sync descriptors, or call any platform/application API. That is not a separate synthetic grant — the platform admin role (kdcube:role:super-admin) is already a grant. So the all-resource token is:

{ "resource_grants": { "*": ["kdcube:role:super-admin"] } }

resource: "*" is deliberately special: it is visible only to users who can delegate kdcube:role:super-admin; it means all platform and application APIs; it still uses the same guard path; the request is projected as the grantor admin user; and the delegated actor is still recorded for provenance. If a token mixes entries, the * entry still matches every resource:

{
  "resource_grants": {
    "*": ["kdcube:role:super-admin"],
    "https://runtime.example/B": ["records:read"]
  }
}
# the * entry matches B too — admin authority on B, because it is a real matching entry
THE GUARD MATCHES ONE RESOURCE ENTRY — NOT A UNION GRANTS DO NOT UNION ACROSS RESOURCES GRANT RECORD A → [read] B → [write] request to A asks: write guard checks row A only 403 forbidden write ∉ A's grants THE WILDCARD * IS A REAL MATCHING ENTRY GRANT RECORD * → [super-admin] B → [read] request to B an admin action guard * matches B ✓ admin on B via the * resource entry Grant checks use only the matching resource entry — {A: read, B: write} never implies write on A.
Grant checks use only the matching resource entry — a denied write on A, and a wildcard * that legitimately matches B.

Configuration: Connection Hub owns the registry

The catalog of delegable grants and protected resources belongs in Connection Hub configuration. Applications may expose managed surfaces, but Connection Hub owns the registry a user sees when creating delegated access.

bundles:
  items:
    - id: connection-hub@1-0
      config:
        connections:
          delegated_credentials:
            oauth:
              enabled: true
              capabilities:
                - { grant: kdcube:role:super-admin, label: Use all platform and application APIs,
                    delegable_roles: [kdcube:role:super-admin] }
                - { grant: news:write, label: Publish news,
                    delegable_roles: [kdcube:role:super-admin] }
                - { grant: conversations:read, label: Read conversations,
                    delegable_roles: [kdcube:role:super-admin] }
              resources:
                - resource: "*"
                  label: All platform and application APIs
                  admin_only: true
                  grants: [kdcube:role:super-admin]
                - resource: "*/api/integrations/bundles/*/*/news@2026-05-20-12-05/operations/publish*"
                  label: News publishing API
                  grants: [news:write]
                  operations:
                    news_publish: { label: Publish news content, grants: [news:write] }
                - resource: "*/api/integrations/bundles/*/*/kdcube-services@1-0/public/mcp/conversations*"
                  label: KDCube conversations MCP
                  grants: [conversations:read]
                  operations:
                    conversations_export: { label: Export conversations, grants: [conversations:read] }

A generic named-services resource may additionally declare named_services.namespaces.<namespace>.tools. The manual screen renders those exact ids as checkboxes. Selecting one adds its declared domain grants and common MCP entry grant; removing a required grant clears the operation. This reuses the existing catalog and creates no synthetic action subtype or parallel grant registry.

This is the same registry used by OAuth delegated credentials and managed MCP. Delegated Access adds a user-created token path on top of that registry.

Configuration: a hosted agent uses per-agent delegation

surfaces:
  as_consumer:
    agents:
      research:
        tools:
          - name: KDCube named services
            kind: mcp
            server_id: kdcube_named_services
            url: https://runtime.example/api/integrations/bundles/demo/project/kdcube-services@1-0/public/mcp/named_services
            resource: "*/api/integrations/bundles/*/*/kdcube-services@1-0/public/mcp/named_services*"
            transport: streamable_http
            delegated: true
            scopes: [named_services:use, slack:read]

url is the endpoint the MCP client dials. resource must byte-match the delegated resource id in Connection Hub's catalog. Grant creation and lookup use that id; the guard matches the request URL against it.

At turn binding the app retrieves the bearer and exact card selector already bound to user + kdcube-agent:<app>:<agent_id> + resource. No token means no server contact. Missing consent therefore fails closed even if the consent presentation is unavailable.

If the operation reaches Slack or Gmail, the user's connected-account consent is a separate Delegated to KDCube boundary. The agent gets only the KDCube bearer, never the provider token.

Configuration: an application REST operation

For an application REST operation, the application declares that its operation is managed by Connection Hub. The application does not parse delegated tokens.

@api(method="POST", alias="news_publish", route="operations")
async def news_publish(self, **params):
    # Product code runs only after the managed REST guard accepts the request.
    ...
bundles:
  items:
    - id: news@2026-05-20-12-05
      config:
        surfaces:
          as_provider:
            api:
              operations:
                news_publish:
                  POST:
                    auth:
                      mode: managed
                      authority_id: delegated_client
                      selected_operation_grants: true
                      operations:
                        news_publish: { label: Publish news content, grants: [news:write] }

selected_operation_grants: true means the operation must be present in the current delegated card and active catalog. MCP calls this protocol-level object a tool; REST calls it an operation. The Connection Hub model is resource and operation based.

Configuration: a platform REST resource

Some APIs are not application operations — they are platform APIs. Connection Hub still owns the resource catalog, and the shared request-auth surface validates the bearer before the normal route handler runs.

connections:
  delegated_credentials:
    oauth:
      capabilities:
        - { grant: devops:deploy, label: Deploy runtime, delegable_roles: [kdcube:role:super-admin] }
      resources:
        - resource: "*/api/platform/admin/redeploy*"
          label: Platform redeploy API
          operations:
            platform_admin_redeploy: { label: Redeploy runtime, grants: [devops:deploy] }

If the URL is not configured as a Connection Hub delegated resource, the bearer token is not treated as magic — normal platform authentication rules apply.

How automation calls it

The call is ordinary HTTP with a bearer token — no browser cookie needed:

curl -sS \
  -H "Authorization: Bearer ${KDCUBE_DELEGATED_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"limit": 5}' \
  "https://runtime.example/api/integrations/bundles/demo/demo/news@2026-05-20-12-05/operations/publish"

The same token can call MCP if its resource_grants and selected operations cover that MCP resource:

POST /api/integrations/bundles/<tenant>/<project>/kdcube-services@1-0/public/mcp/conversations
Authorization: Bearer kst1...

tools/call conversations_export

Failure modes

The useful failures are explicit:

401 unauthorized
  no bearer token, expired token, revoked token, or unknown token

403 forbidden
  resource_grants has no matching resource entry
  matching resource entry lacks the required grant
  selected operation/tool is absent
  grantor can no longer delegate the grant
  selected capability is no longer in the active catalog

503 temporarily unavailable
  current card or complete active catalog cannot be obtained and validated

The important security rule — grant checks use only the matching resource entry, never a union of all grants on the token:

resource_grants:  A: [read]   B: [write]
request to A with write   ->   forbidden

What to test

  1. Sign in as a regular user; open Connection Hub → Delegated by KDCube.
  2. Confirm all-resource admin access is not visible.
  3. Create a token for one concrete resource; call it with Authorization: Bearer ....
  4. For named-services MCP, select only mem.object.search. Confirm search succeeds while memory write/action and every other namespace fail closed.
  5. Try to submit a named-service operation without its declared grant and confirm token creation fails.
  6. Call a different resource and confirm it fails. Revoke the card and confirm the next invocation fails while already admitted work may finish.
  7. Connect an external MCP client through the OAuth consent flow; confirm it appears under Granted access as a connected app; revoke it and confirm the client's next call is rejected.
  8. For a provider-backed namespace, revoke its connected-account claim while retaining the KDCube grant and confirm the provider call fails.
  9. Sign in as an admin; create an all-resource token with kdcube:role:super-admin; call an admin-only API.
  10. Confirm logs show delegated projection and the automation actor.
  11. Configure two hosted agents for the same resource; grant one and confirm its sibling remains unbound.
  12. Confirm a later turn reuses the bound token without exposing it or the user session to the model.
  13. Make resource differ from the catalog id and confirm lookup fails before MCP contact.
  14. Revoke one agent-resource row and confirm only that connection stops.
  15. For a provider-backed call, revoke the agent grant and connected-account claim independently; either must stop it.
  16. Remove a selected capability from the active catalog and confirm the next call reports catalog drift while the card preserves the old selection.
  17. Delete the Redis card/catalog projection, retain durable storage, and confirm the next call rebuilds the projection and reaches the same decision.
# REST
[connection-hub.oauth.rest_guard] accepted ...
Managed REST runtime projection applied ...

# MCP
[connection-hub.oauth.mcp_guard] accepted ...
Managed MCP runtime projection applied ...

Why this belongs in Connection Hub

This is not a one-off token feature — it is another way to create a delegation edge. Connection Hub already owns who the platform user is, which grants they may delegate, which resources/operations are delegable, where immutable card revisions, catalog versions, and current pointers live, and how the runtime projects actor, grantor, and economics identity. Delegated Access reuses that infrastructure; the application owns product code, Connection Hub owns the boundary.

THREE ENTRY PATHS, CURRENT AUTHORITY PER INVOCATION Claude OAuth / MCP connector negotiates a credential via consent an external client User-created automation token Delegated Access → Bearer the direct user-created path Hosted KDCube agent per-user · per-agent card kdcube-agent:<app>:<agent> Connection Hub · current card + active catalog exact access_id · resource · operation · grants + explicit inner admission for named services product code grantor context · delegated actor preserved
Three delegated-client paths, one durable card/catalog authority, and one decision per invocation.

Related publications

Protecting KDCube Surfaces With Managed Credentials — the managed guard that checks resource_grants and the selected operation/tool before product code.
Authenticated MCP In KDCube: Delegated Credentials, Not Shared Secrets — the OAuth/MCP version of delegated credentials.
Delegating A KDCube Service To An External App — the user-facing delegation lifecycle.
Setting Up Platform Authority In KDCube — why the approving user can come from Cognito, multi-Cognito, or an application-hosted authority.
Connect Your Named Services To Claude Code — a concrete external-agent consumer of the same managed delegated-credential model.
App as MCP Provider and Consumer — the end-to-end builder recipe for consuming MCP, exposing an app MCP surface, and issuing granular automation access.
KDCube Deep · 04.07.2026