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
        |
        | guard checks resource_grants + selected operation
        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 server-side on every call.

The whole idea in one line

Connection Hub lets a signed-in user mint a short-lived bearer token for automation, where the real authority is stored server-side as resource_grants: resource → grants[], plus selected operations. 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 + namespace ops Connection Hub Delegated Access stores the grant record Automation script · agent · CI Bearer kst1… managed surface REST · MCP · platform guard checks the record create token (once) Bearer revoke anytime SERVER-SIDE GRANT RECORD the token is only a handle · loaded per call news publish → [news:write] named services → mem:[object.search] The bearer is a handle — runtime enforces resource_grants and the narrowed named_services policy. Product context derives from the grantor; the automation remains the delegated actor for provenance.
The bearer token is only a handle; the guard loads the server-side grant record and enforces resource-grant matching per call — and the user can revoke it anytime.

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 is immediate for all three paths. A manual token's bound session is logged out. An OAuth connect loses its refresh token (no new access tokens) and its current access-grant binding — the bearer in flight is rejected by the guards.

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
  |  hash token -> load server-side grant record
  |  check resource_grants against current URL/resource
  |  check selected operation/tool
  |  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 binding gate: a bearer without its bound server-side grant record fails closed.

The data model

The important persisted shape is resource_grants. The bearer token is only a handle — runtime guards read the server-side record by token hash.

{
  "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.

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 existing operation ids against the live descriptor and stores a narrowed copy of the same named_services policy in the server-side grant record. The KDCube Services bridge reads that stored tree, so an unticked namespace or operation fails even though it uses the same generic MCP URL. Provider-account claims are 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 immediate — the guard must load the server-side record.
  • 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 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 server-side delegated credential record. 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

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 token and confirm the same call now fails.
  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.
# 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 durable consent and delegated credentials are stored, 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, ONE GUARD CONCEPT 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 grant kdcube-agent:<app>:<agent> Connection Hub · delegated credential store + guard checks resource_grants + compatible operation + narrowed named-service policy when applicable product code grantor context · delegated actor preserved
Three delegated-client paths, one registry and guard — but separate bound grants and preserved actors.

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