1. Edge & access control
| Capability | Owner | Where the seam lives |
|---|---|---|
| HTTP / SSE / Socket.IO | Platform | chat-ingress (port 8010) terminates HTTP / SSE / Socket.IO; chat-proc (port 8020) mounts bundle REST & MCP under /api/integrations/bundles/{tenant}/{project}/{bundle_id}/…. Bundles never bind a port. |
| JWT / Cognito / SSO | Platform | Ingress validates the token, mints a conversation id, and threads (tenant, project, user) into runtime context. Normal bundle workflows receive identity context rather than owning raw JWT parsing. |
| Antivirus / file scan | Platform | ClamAV in chat-ingress, pre-admission. Uploads are scanned before any bundle code runs. |
| Multi-tenancy & RBAC | Shared |
PlatformEnforces the tenant boundary at the gateway, in runtime context, in Postgres, in file paths, and in Redis namespaces (
infra/namespaces.py).BundleDeclares role gates on each decorator (
allowed_roles, user_types); the platform enforces them. |
The edge is fully platform-owned for one practical reason: it's the part that has to be uniformly safe across every bundle on the runtime. A bundle that authored its own JWT path or its own ClamAV bypass would compromise the whole tenant. So the seam is high — the bundle starts after ingress has authenticated, scanned, and routed. See Communication for the full ingress shape and Deployment for the multi-tenant runtime.
2. Isolation & secrets
| Capability | Owner | Where the seam lives |
|---|---|---|
| Code execution / sandbox | Platform | py-code-exec sidecar with the UID-1001 split-container model. Bundle calls into it via run_exec_tool / build_exec_output_contract in chat/sdk/tools/exec_tools.py. Bundle never sandboxes itself. |
| Secrets (sync + async) | Platform | infra/secrets/manager.py resolves named secrets and audits the read. Bundle pulls via get_secret(...) from chat/sdk/config.py. Raw env vars and KMS handles never reach bundle code. |
| Per-task dependencies | Bundle | @venv(requirements=[…]) runs a function in a per-bundle subprocess venv (hash-cached). Bundle picks the deps; platform builds + caches. |
The isolation seam is the topic of an earlier post in this series: the executor runs without network or platform secrets, and a trusted supervisor mediates approved tool calls. From the bundle author's side, the contract is one function call. Everything beneath — split containers, supervisor RPC, network policy, syscall surface — is platform infrastructure. See also Security.
3. Economics, observability, governance
| Capability | Owner | Where the seam lives |
|---|---|---|
| Budgets & accounting ledger | Shared |
PlatformReservation-then-commit, wallet, ledger in
infra/accounting/ and chat/sdk/infra/economics/. Admits, meters, and rejects atomically.BundleDeclares
app_quota_policies in configuration() — what each tier is allowed to spend. |
| Metrics service | Platform | Dedicated metrics service on port 8002 with turn-level cache and aggregator. |
| Structured logging | Platform | AgentLogger is injected into bundle context. Bundle logs through the injected logger; correlation ids and turn ids are attached automatically. |
| Audit trail | Platform | Per-turn append-only audit on every admission decision, tool call, model call, and reservation/commit. |
Economics is the most consequential shared seam on the platform. The bundle controls policy (a free tier gets 50 model calls/day; an enterprise tier gets per-project budgets per skill); the platform owns enforcement (atomic Redis-Lua reserve, ledger commit, hard reject). Bundles can't accidentally bypass quotas because the admission check sits outside their code path. Economics covers the full ledger model.
4. Storage, knowledge, retrieval
| Capability | Owner | Where the seam lives |
|---|---|---|
| Artifact & file storage | Shared |
PlatformOwns the backends —
storage/storage.py, AIBundleStorage, knowledge space, conversation index. Bundle code normally goes through SDK storage/retrieval APIs rather than S3 clients or ad hoc pgvector access.BundlePicks layout and what to write — paths for turn artifacts, naming conventions for uploads, KB source globs.
|
| Knowledge base / pgvector RAG | Platform | Schema, embedding service (infra/embedding/), retrieval pipeline. Bundle configures sources and the embedding model; ingestion + search are platform calls. |
| Citation / source pool | Platform | Citation pool + provenance live in chat/sdk/retrieval/. Bundle emits citation deltas through self.comm; rendering and pool maintenance are platform. |
The storage seam is deliberately a layer: the bundle never picks an S3 bucket name or talks to pgvector directly, but it does name its own paths, pick its own KB sources, and decide what to write. Concepts introduces the artifact taxonomy that the platform's source pool, turn log, and citation system depend on. ReAct's conversation memory layers use the same artifact/provenance discipline; the standalone User Memory subsystem adds user-owned durable records on top when a bundle enables it.
5. Lifecycle & configuration
| Capability | Owner | Where the seam lives |
|---|---|---|
| Bundle loader & hot-reload | Platform | infra/plugin/agentic_loader.py, bundle_store.py, bundle_registry.py, git_bundle.py. kdcube reload <bundle_id> swaps the bundle module in place without restarting the runtime. |
| Descriptors → props → overrides | Shared |
PlatformResolves
assembly.yaml → bundles.yaml → Redis broadcast → bundle defaults, then re-broadcasts effective props on every admin override.BundleReads
self.bundle_props — never the descriptor files directly (that's an anti-pattern called out in agents.json). |
| One-time init | Bundle | async on_bundle_load(...) runs once per process per (tenant, project). Default implementation builds the bundle UI if declared. |
| Live prop changes | Bundle | async on_props_changed(*, previous_props, current_props, reason, …) fires after effective props change. For cache busts only — not heavy work. |
Hot-reload is the property that makes the whole bundle model practical: an admin pushes a new bundle version, the loader registers the new module, in-flight turns drain, new turns admit against the new code. The bundle doesn't write any of that. What the bundle does write is the two lifecycle hooks above — both of them small, both of them optional. See Configuration for the descriptor / prop / override flow.
6. Scheduling & background work
| Capability | Owner | Where the seam lives |
|---|---|---|
| Scheduled jobs | Shared |
PlatformProc-resident scheduler (
chat/sdk/processor_scheduler_backend.py) with Redis lock semantics — span={system | instance | process} guarantees a single owner per (span, job) across replicas.BundleDecorates a method
@cron(...) with expr_config, tz_config, and a span. The platform gates it through the canonical enabled.cron.<alias> bundle prop. |
| Background job stream | Shared |
PlatformOwns
RedisBackgroundJobStream — fair claim across procs, retry semantics, progress / log channel to the chat surface.BundleEnqueues work through the jobs stream and consumes it via one async
@on_job(...) dispatcher. SDK mixins expose handle_job(...) helpers instead of adding more decorated handlers. |
Off-turn work uses the same handoff pattern as the rest of the platform: span-aware cron, a Redis-locked scheduler, and a generic background job stream. Together they let a bundle declare a nightly KB re-index, a durable maintenance job, or an event-driven background workflow without standing up its own queue. User Memory reconciliation currently uses widget-submitted background work plus the bundle's single @on_job dispatcher; a bundle could also schedule similar maintenance with @cron.
7. Communication surfaces (REST / widgets / UI / MCP)
| Capability | Owner | Where the seam lives |
|---|---|---|
| REST endpoints | Shared |
PlatformMounts the method under
/api/integrations/bundles/{tenant}/{project}/{bundle_id}/{operations|public}/{alias}, enforces role gates, and handles the chosen auth mode (none / bundle / header_secret).BundleDecorates the handler
@api(method, alias, route, user_types, roles, public_auth). |
| Dashboard sidebar widgets | Shared |
PlatformThe chat-web-app renders the widget card in the sidebar and threads the bundle's
@api behind it.BundleStacks
@ui_widget(icon, alias, ...) on top of the underlying @api. |
| Source-folder UI | Shared |
PlatformBuilds declared source folders, materializes optional
shared_sources, stores built assets under bundle storage, and serves normal or public widget routes with SPA fallback.BundleShips React/Vite or HTML source under
ui.main_view or ui.web_app_widgets.<alias>, and uses the runtime config handshake for tenant/project/bundle-aware API calls. |
| Public widget launch | Shared | For Telegram Mini Apps and similar surfaces, the platform serves static assets from /public/widgets/{alias}. The bundle must protect every public data/action API itself, for example by verifying Telegram WebApp initData. |
| MCP server | Shared |
PlatformMounts the bundle-served MCP endpoint with
streamable-http transport, negotiates the session, and proxies the wire protocol.BundleDecorates the method
@mcp(alias, route, transport='streamable-http') and owns the MCP request auth check inside the method — validates request.headers itself. Public APIs can make the same choice with bundle-owned auth; operations APIs stay platform-authenticated. |
| MCP client (tools) | Platform | create_tool_subsystem_with_mcp wires upstream MCP servers into the bundle's tool registry. |
The same decorator-driven model surfaces REST, widgets, UIs, and MCP. The bundle author writes the handler and the widget source; the platform handles routing, role enforcement, static-asset serving, the chat-web-app integration, MCP transport negotiation, and source-folder builds. Public widget routes are only for loading the app shell; product data still goes through an authenticated bundle API. Integrations documents the surface in depth, and Communication covers the streaming contracts the chat surface uses.
8. Agent runtime, memory, tools, skills, streaming
| Capability | Owner | Where the seam lives |
|---|---|---|
| Agent loop (ReAct v3) | Shared |
PlatformShips ReAct v3 with a timeline-first turn model, continuations, cache/compaction discipline, tool scheduling, artifact streaming, and a custom channeled protocol. ReAct is not provider-native tool calling: the runtime parses channels such as thinking, structured decisions, code, artifacts, and other typed events, then schedules them.
BundleEither inherits the ReAct-capable entrypoint stack or builds its own loop inside
execute_core. The bundle still receives the same runtime context either way. |
| ReAct memory model | Platform | Timeline blocks, turn log, sources pool, working summaries, compaction summaries, indexed internal notes, workspace refs, and read/search tools are platform-owned ReAct memory layers. |
| User Memory subsystem | Shared |
PlatformOwns the durable user-memory tables, scoring/search/store layer, Memory widget, snapshots, restore, and reconciliation job mechanics.
BundleOpt-in by deriving from the memory entrypoint mixin and configuring
memory.{announce,tools,widget,reconciliation,snapshots}. ReAct can consume the hotset/search tools when enabled. |
| Tool registry | Shared |
PlatformOwns the tool subsystem, SDK tools, MCP adapters, runtime binding, and approved tool execution in the main process, subprocess runtime, and isolated generated-code runtime. This is a security boundary: generated code can call approved KDCube tools through the runtime, not arbitrary platform internals.
BundleShips its own
tools/ package plus tools_descriptor.py for runtime registration, aliases, MCP exposure, and per-tool runtime policy. |
| Skill resolution | Shared |
PlatformOwns the KDCube skills registry and resolver in
chat/sdk/skills/skills_registry.py. Skills are KDCube instruction bundles defined with SKILL.md metadata, selected per consumer, and injected into ReAct, codegen, or content generators.BundleShips
skills/ plus skills_descriptor.py; declares which custom skills exist, which imports they allow, and which skills are visible per role or surface. |
| Channeled streaming & saturated widgets | Shared |
PlatformOwns the channeled streamer, repeated-channel instance handling, citation replacement, and live widget adapters. Saturated widgets such as exec and web search receive structured parts as soon as they are detected: objective, code, contract, execution status, search queries, result rows, and fetch/read results.
BundleReuses SDK widgets or adds bundle-specific subscribers and UI components. The bundle defines the domain payload; the platform handles transport, ordering, storage, and live delivery.
|
| Multimodal context & artifacts | Shared |
PlatformNormalizes text, uploads, images, files, generated artifacts, source previews, and tool outputs into the turn timeline and artifact store so agents can reason over multimodal context without each bundle reinventing storage or preview logic.
BundleDecides which media types it accepts, which renderers/tools it exposes, and how the UI should present bundle-specific artifacts.
|
| LLM router / providers | Platform | ModelServiceBase in infra/service_hub/inventory.py; provider matrix in chat/sdk/providers.py; infra/llm/ for the transport. Bundle picks role models in configuration(). |
The agent runtime is where the platform offers the most and the bundle can still replace the most. A ReAct bundle gets the layered conversation memory model, cache discipline (covered here), attention area (here), and channel-aware streaming (here). It also gets the KDCube skills subsystem, approved tool execution across runtimes, multimodal turn context, and live saturated widgets that behave like client-side components while being driven by model/tool events. A bundle that derives from the memory mixin can expose user-owned durable memories through the Memory widget, optional hotset projection into ReAct ANNOUNCE, and optional memory search/read/write tools. A bundle that needs a different loop overrides execute_core and drives its own LangGraph; storage, auth, economics, tools, skills, widgets, jobs, and observability still come from the platform.
9. The SDK contract at a glance
Across all eight sections above, the bundle author writes against a small, stable surface. Here are the decorators and lifecycle hooks. Every one of them is documented in the SDK reference; their full signatures live in kdcube_ai_app/infra/plugin/agentic_loader.py.
| Decorator / hook | What one line tells the platform |
|---|---|
@agentic_workflow | This class is a bundle entrypoint. Inject (config, pg_pool, redis, comm_context, …). |
@bundle_id("id@version") | Declarative identity for the loader. |
@api | Mount this method as a REST endpoint with role gating + auth mode. |
@ui_widget | Surface the @api in the chat sidebar with an icon. |
@mcp | Serve this as an MCP endpoint (bundle-owned auth). |
@cron | Run periodically with a span; platform locks across procs and gates through enabled.cron.<alias>. |
@on_job | Consume from the background job stream. |
@venv | Run this function in a subprocess venv with declared deps. |
configuration | Role models, embedding, execution, UI, MCP services, quota policies, optional memory subsystem config. |
on_bundle_load | One-time init per process per (tenant, project). |
on_props_changed | Cache-bust hook after admin overrides. |
execute_core | The agentic entrypoint — the actual turn. |
enabled.bundle, enabled.api["alias.METHOD"], enabled.widget.alias, enabled.mcp.alias, and enabled.cron.alias. Missing keys mean "use the decorator/code default"; explicit falsy values return 404 for API/MCP/widget surfaces, skip cron reconciliation, or disable the whole bundle. The same effective-props mechanism covers every surface a bundle exposes.
/operations/{alias} with platform auth; Telegram mode calls /public/{telegram_alias} and sends verified initData.
10. Two bundles, same seam
The same boundary supports both ends of the spectrum.
On one end: kdcube.copilot is a focused documentation-reader bundle. It prepares a ks: knowledge space, exposes curated docs/source paths, and lets ReAct answer product and architecture questions with precise references. The same bundle can now opt into economics, durable User Memory, and a Telegram WebApp with two sections — memory management and Telegram user administration — without owning a separate server.
On the other end: the reference versatile bundle is a larger example application. It builds its own LangGraph StateGraph, runs an MCP server with a header-token auth scheme, ships tools_descriptor.py and skills_descriptor.py, integrates Telegram delivery, registers widgets, exposes the shared User Memory widget, and demonstrates bundle-owned tools. The bundle author wrote application logic. The platform — the same platform — supplied ingress, auth, accounting, isolation, hot-reload, observability, the loader, and the SDK seams the bundle decorates against.
The handoff is clearest in two places. First, BaseEntrypoint.__init__ in chat/sdk/solutions/chatbot/entrypoint.py — the constructor shows the platform-injected runtime inputs (config, pg_pool, redis, comm_context, event_filter, ctx_client, continuation_source). The base class then initializes platform helpers such as logger, models_service, kv_cache, and effective bundle_props. The bundle decides what to do with that context.
Second, the loader: agentic_loader.py introspects the bundle's decorators and registers routes, schedules, MCP transports, and widget cards on the runtime. The bundle never registers a URL itself. One decorator line is the entire contract for "mount me here, with these roles, gated by this flag."
Where to go next
If you're sizing KDCube against your problem: Platform overview for the system shape, SDK reference for the decorator surface, Configuration for descriptors and props, Economics for the budget model, Security for the isolation story, and Deployment for the multi-tenant runtime.
If you're a coding agent: agents.json is the machine-readable manifest with the full decorator list and source paths; AGENTS.md is the human-readable companion. Parse one, read the other.