docs/architecture/phase-1-spec.md. This page is rendered from the source document during the MkDocs build.Phase 1 Architecture Specification
Objective
Phase 1 proves one complete platform loop:
The deliverable is an Agent Operating Platform slice, not a marketplace catalog page. A user must be able to publish a versioned Agent Card, discover it by capability, install it into a workspace, invoke it through the A2A Router, and inspect the complete invocation lineage.
Product Boundary
Phase 1 operates independently implemented Agents as protocol-facing black boxes. Agent Runtimes own model, prompt, tool, planner, workflow, memory, RAG, session, and runtime telemetry behavior. NeKiro owns publication, discovery, Workspace authorization, exact-version resolution, managed routing, and platform-level invocation lineage.
Full Agent Runtime frameworks must not become Control Plane or A2A Router core dependencies. They may be used by sample Agents or isolated adapters. The Phase 1 proof uses at least two sample Agents backed by different Runtime implementations so the platform cannot pass acceptance by relying on one framework's internal types or storage.
Deployment Units
- NeKiro-Console is the production user interface.
apps/control-planeis one Go process containing Gateway, Catalog, Workspace, and Invocation Dispatch modules.apps/a2a-routeris an independent Go data-plane process.- NeKiro-Samples owns the independent sample A2A servers.
- PostgreSQL is the target persistent store. Logical module ownership applies even when modules share one database instance.
Ownership
| Module | Owns | Must not own |
|---|---|---|
| Gateway | Northbound HTTP boundary, caller/workspace context, response shape | Agent Card persistence, A2A transport |
| Registry | Agent Card versions and publication state | Running agents, invocation execution |
| Discovery | Query projection derived from published cards | A second source of truth |
| Workspace | Installations and accepted permissions | Agent deployment |
| Invocation Dispatch | Invocation identity and pre-dispatch authorization | A2A protocol execution |
| A2A Router | A2A transport, transient result forwarding, context propagation, timeout/cancel, event emission | Permanent Agent Card ownership, Registry or Workspace storage access |
| Ledger | Append-only invocation events and query projection | Routing or authorization decisions |
Contract Sources
Cross-language contracts are owned by language-neutral artifacts:
contracts/schemas/contains versioned JSON Schema documents.contracts/openapi/control-plane.v3.yamldefines the active Catalog, Discovery, Workspace, and Installation Northbound API;control-plane.v2.yamlremains unchanged migration evidence. Any legacy Invocation paths still present in the v3 document are migration evidence and are not served by the current Gateway.contracts/openapi/public-agent-share.v1.yamlandcontracts/schemas/public-agent-share.v1.schema.jsondefine the anonymous public Agent identity projection.GET /v4/public/agents/:publicAgentIdexposes only the canonical public URL and eligible published trusted Release facts; it never exposes an endpoint, binding, evidence, credential, Workspace, or Ledger data.contracts/openapi/control-plane-invocation.v4.yamldefines the active Invocation and Trace Northbound API.contracts/openapi/control-plane-internal.v2.yamldefines Router-to-Control Plane exact Agent resolution;control-plane-internal.v3.yamldefines nested installed-version resolution.contracts/openapi/router-internal.v4.yamldefines active Control Plane-to-Router dispatch and result transport;router-metadata.v3.yamlis the active Workspace-scoped Invocation/Trace read contract, while the completerouter-internal.v3.yamlis historical migration evidence.contracts/openapi/router-topology-status.v1.yamldefines the authenticated, read-only Router-local watched-topology status used to prove exact-Release lifecycle consumption without exposing endpoints or provider revision tokens.contracts/a2a-profile/v0.3.0/profile.v0.2.jsonpins the active supported A2A subset and context headers.contracts/router-agent-credential/v1/andcontracts/schemas/router-agent-credential.v1.schema.jsondefine the separately versioned signed Router-to-Agent request binding without changing A2A Profile Schema0.2.contracts/*.gomaps these contracts into Go and verifies the mapping against the source schemas.
Go and TypeScript types are consumers of these artifacts, never competing sources of truth. Services must not exchange internal implementation types across a process boundary.
Historical v1 files remain unchanged as migration evidence. The first backend runtime implements only the active versions and does not introduce speculative dual-version behavior.
Router-to-Agent authentication
The Router signs a fresh compact Ed25519 JWT for every A2A HTTP request. The
credential binds the canonical endpoint origin, exact Agent/Card release,
Workspace authorization context, capability, Invocation/Task/Trace lineage,
and optional parent Invocation. Agents accept one configured issuer, audience,
key ID, and public key, reject replayed jti values atomically in-process, and
execute runtime logic only after all claims match single-valued context
headers. Stream and cancel requests use different credentials. No credential,
key, signature, or jti enters Agent Card, result, event, or Ledger storage.
Northbound API v3: Catalog and Workspace
| Method | Path | Purpose |
|---|---|---|
POST |
/v3/agents |
Register a draft Agent Card v0.2 version |
POST |
/v3/agents/:agentId/versions/:version/publish |
Publish an immutable version |
POST |
/v3/agents/:agentId/versions/:version/disable |
Disable a version for new resolutions |
GET |
/v3/agents |
Discover published agents by query/capability/owner |
GET |
/v3/agents/:agentId/versions/:version |
Read an exact Agent Card version |
POST |
/v3/workspaces |
Create a minimal owner-controlled Workspace |
GET |
/v3/workspaces/:workspaceId |
Read an owned Workspace |
POST |
/v3/workspaces/:workspaceId/installations |
Install and accept declared permissions |
GET |
/v3/workspaces/:workspaceId/installations |
List current and historical Installations |
GET |
/v3/workspaces/:workspaceId/installations/:installationId |
Read one exact Installation |
PATCH |
/v3/workspaces/:workspaceId/installations/:installationId |
Enable or disable an installation |
DELETE |
/v3/workspaces/:workspaceId/installations/:installationId |
Uninstall and return preserved history |
Public sharing is a read-only Catalog projection, not a second installation or invocation boundary:
| Method | Path | Purpose |
|---|---|---|
GET |
/v4/public/agents/:publicAgentId |
Resolve a stable public Agent ID anonymously to exact selectable Releases |
The Console requires an exact configured VITE_NEKIRO_PUBLIC_AGENT_ORIGIN and
accepts only canonical /a/:publicAgentId URLs. Installation reuses the
authenticated /v3/workspaces/:workspaceId/installations contract, so the
public URL never reaches Agent transport and never authorizes a Workspace.
The Gateway returns Platform Error v2 for Catalog failures and Platform Error v3
for Workspace/Installation failures. Public messages are fixed by error code and
cannot contain internal dependency errors, credentials, request payloads, or
Agent output. INSTALLATION_DISABLED identifies Workspace authorization state
while AGENT_DISABLED identifies Catalog version state.
Northbound Invocation API v4
| Method | Path | Purpose |
|---|---|---|
POST |
/v4/workspaces/:workspaceId/invocations |
Authorize, dispatch, and return a transient JSON or SSE result |
GET |
/v4/workspaces/:workspaceId/invocations/:invocationId |
Read one Workspace-scoped invocation and metadata-only Ledger events |
GET |
/v4/workspaces/:workspaceId/traces/:traceId |
Read Workspace-scoped metadata-only parent/child invocation lineage |
The Invocation Gateway uses Platform Error v4 after the runtime acceptance boundary. Trace correlation is required; Invocation and root Task correlation are present together after Invocation creation. Dependency failure must never be represented as not found, an empty list, or success.
Directional Internal APIs
| Method | Path | Owner | Purpose |
|---|---|---|---|
POST |
/internal/v2/resolve-agent |
Control Plane | Resolve an authorized installed exact Agent Card v0.2 and capability |
POST |
/internal/v3/resolve-installed-version |
Control Plane | Resolve the exact enabled Installation pin for a nested call |
POST |
/internal/v4/invocations |
Router | Execute an authorized root invocation and return a transient JSON or SSE result |
GET |
/internal/v3/workspaces/:workspaceId/invocations/:invocationId |
Router | Read Workspace-scoped metadata-only Invocation detail |
GET |
/internal/v3/workspaces/:workspaceId/traces/:traceId |
Router | Read Workspace-scoped metadata-only lineage |
Control Plane Internal v2/v3 are served by the Control Plane and called by the Router. Router Internal dispatch v4 is served by the Router and called by the Control Plane. Their server destinations are distinct and explicitly configured. The Router resolves cards through the internal Control Plane API and must not query Registry or Workspace tables directly.
Invocation Result Delivery
POST /v4/workspaces/:workspaceId/invocations is the only Northbound result
channel. stream=false returns one application/json Invocation Result v1.
stream=true returns ordered text/event-stream Invocation Result Stream
Event v2 values on the same response. The request mode and Accept header must
agree; mismatch returns 406 NOT_ACCEPTABLE.
A clean stream begins with accepted and ends with exactly one completed,
failed, canceled, or timed_out event. Event and chunk order is zero-based
and monotonic. The first terminal outcome is immutable. EOF before a terminal
event is interrupted delivery, and chunks before a non-success terminal event
are incomplete output.
Results and chunks are transient arbitrary JSON values constrained by the resolved Skill output schema. Phase 1 has no result persistence, polling, replay, reconnect cursor, or result query endpoint. A caller may inspect Ledger facts after disconnect but must create a new Invocation to receive output.
Invocation Lifecycle
Every invocation carries invocation_id, root_task_id, trace_id, and an optional parent_invocation_id. Agent-to-Agent calls create child invocations through the Router and preserve all lineage identifiers.
Ledger writes are append-only Invocation Event v0.3 facts. Terminal event type,
status, and error code must agree: TIMEOUT belongs only to timed_out,
CANCELED belongs only to canceled, and failed excludes both. Agent input,
result, and chunk content are forbidden. A mutable read projection may be
derived from events, but it cannot replace the event history.
A2A Profile
Phase 1 targets A2A 0.3.0 over JSON-RPC using github.com/a2aproject/a2a-go v0.3.15. Agents expose /.well-known/agent-card.json and support:
message/sendmessage/streamtasks/gettasks/cancel
Platform trace and workspace context uses the headers declared in contracts/a2a-profile. Authentication credentials are resolved at the Router boundary and must never be persisted in Agent Card or Ledger payloads.
Acceptance
Cross-component acceptance is owned by NeKiro-Stack, which pins exact Core, Console, SDK, Samples, and transport revisions before running the suite.
The final E2E suite must prove:
- A valid card can be registered and published; invalid cards and cards containing undeclared fields are rejected.
- Discovery only returns eligible published versions and can filter by capability.
- Installation requires explicit permission acceptance and resolves an exact version.
- An installed agent can be invoked through the Router and returns the exact non-streaming result or ordered streaming result events through the Gateway.
- An uninstalled, disabled, or unauthorized agent is rejected before dispatch.
- Two sample Agents backed by different Runtime implementations can be registered and installed; Agent A can call Agent B through the Router and produce a complete parent-child trace without shared Runtime internals.
- Timeout, cancellation, route failure, A2A failure, and agent failure remain distinguishable in Ledger without persisting Agent input or output.
- Router resolution reaches only the Control Plane destination, while dispatch and Ledger queries reach only the Router destination.
- Both sample Agents pass the same A2A Profile conformance and are invoked without framework-specific Control Plane or Router behavior.