A runtime-agnostic framework for registering, discovering, installing, invoking, and auditing Agents.
Documentation · 简体中文 · Architecture · Contracts · Samples · Stack
NeKiro provides the platform layer around Agent runtimes. It gives Agents a versioned identity, makes exact releases discoverable and installable, routes managed calls through a controlled A2A boundary, and records invocation lineage without taking ownership of the Agent's internal execution model.
Use NeKiro when different Agent frameworks or services need to cooperate under one consistent contract and security model.
Agent runtimes such as tRPC-Agent-Go are responsible for models, prompts, tools, planners, workflows, memory, RAG, and sessions. NeKiro does not replace those capabilities. It connects independently implemented Agents through a framework-owned lifecycle:
Register -> Discover -> Install -> Invoke -> Record
- Runtime agnostic: an Agent may use tRPC-Agent-Go,
a2a-go, another framework, or a custom runtime. - Contract first: Agent Cards, Releases, HTTP APIs, internal APIs, A2A profiles, credentials, events, and results are independently versioned.
- Router mediated: managed Consumer-to-Provider and Agent-to-Agent calls go through the A2A Router instead of using direct Provider addresses.
- Exact release routing: discovery and invocation preserve Agent version, Release ID, Card digest, endpoint provenance, and audience.
- Workspace governance: discovering an Agent does not automatically grant permission to invoke it; installation and permissions are explicit.
- Auditable lineage: nested calls propagate
root_task_id,parent_invocation_id, andtrace_idinto an append-only metadata Ledger. - Fail closed: missing topology, invalid configuration, failed trust checks, and unavailable dependencies remain distinct failures without retry, alternate endpoints, or stale-success fallback.
| Stage | What happens | Owning boundary |
|---|---|---|
| Register | A Provider publishes an Agent Card and an immutable Release, then registers a ready runtime instance. | Catalog Registry + Provider deployment |
| Discover | Consumers query published Agent capabilities and exact versions. | Gateway + Catalog |
| Install | A Workspace authorizes an exact Agent version and required permissions. | Workspace |
| Invoke | Gateway dispatches the request to the A2A Router, which resolves the exact Release and a ready instance. | Gateway + A2A Router |
| Record | The Router appends status, failure, timing, Release provenance, and task/trace lineage. | Invocation Ledger |
The current product Stack verifies the complete lifecycle against PostgreSQL, Nacos, the production Console, and two independently implemented sample runtimes. Core also provides explicit private-CA TLS and mTLS configuration for the Router's Nacos HTTP and gRPC transports; exercising those Router-side modes in the deployed Stack is the next acceptance milestone.
- Trusted Agent Card registration and immutable Release publication.
- Capability discovery and public Agent sharing.
- Workspace installation with explicit permissions.
- Exact-Release instance registration and Nacos lifecycle observation.
- Atomic initial-snapshot/watch handoff and fail-closed empty topology.
- Runtime removal, replacement, and routing recovery.
- JSON invocation and Server-Sent Events streaming.
- Cancellation propagation to the Provider.
- Nested Agent calls that re-enter the Router in both directions.
- Queryable Invocation and trace lineage in PostgreSQL.
- Router-issued, short-lived Agent credentials.
- Explicit private-CA TLS and mTLS configuration and Core tests for Router Nacos HTTP/gRPC transports.
- Product E2E coverage for Provider Nacos TLS and mTLS registration.
- Linux and Windows Config Center verification.
The Stack acceptance deliberately proves that Consumers cannot bypass the Router, removed instances do not receive new calls, and secrets or Agent payloads are not written to the Ledger.
flowchart LR
User["User / Console / Application"] --> Gateway["Control Plane Gateway"]
Gateway --> Catalog["Catalog Registry"]
Gateway --> Workspace["Workspace"]
Gateway --> Router["A2A Router"]
Provider["Provider Agent"] -->|"Card + Release"| Gateway
Provider -->|"Instance lease"| Nacos["Nacos"]
Nacos -->|"Snapshot + watch"| Router
Router -->|"Managed A2A call"| Provider
Provider -->|"Nested managed call"| Router
Router --> Ledger["Invocation Ledger"]
Catalog --> Postgres[("PostgreSQL")]
Workspace --> Postgres
Ledger --> Postgres
The deployment rules are intentionally strict:
- Console and external applications access the platform through Gateway.
- Gateway delegates managed Agent execution to the A2A Router.
- Registry remains the only permanent source of Agent Card and Release facts.
- Router resolves exact Releases and ready instances without storing a second permanent Card.
- Agent-to-Agent calls re-enter Router and preserve task/trace lineage.
- Ledger stores metadata facts, never Agent input, output, credentials, or secrets.
See Platform Direction and the Phase 1 Architecture for the complete ownership and trust model.
NeKiro-Samples contains two Agents with the same platform contract but different runtime implementations:
| Sample | Runtime | Demonstrates |
|---|---|---|
| Runtime A | tRPC-Agent-Go | A framework-backed Agent, TLS Nacos registration, Router-mediated invocation, and nested calls to Runtime B. |
| Runtime B | Direct a2a-go server |
JSON/SSE/task/cancellation behavior, mTLS Nacos registration, exact instance identity, and nested calls to Runtime A. |
The Stack also exercises a replacement Runtime B instance and Provider registration security fixtures for wrong CAs, wrong TLS server names, and missing mTLS client identities.
The resulting sample flow is:
Runtime A -> Router -> Runtime B
Runtime B -> Router -> Runtime A
Neither runtime receives the other's direct target address.
Start here if you want to see the complete Register -> Discover -> Install -> Invoke -> Record loop. The immutable NeKiro-Stack prepares exact Core, SDK, Samples, and transport revisions, starts PostgreSQL and secured Nacos, and runs the backend acceptance. Git, Go 1.26+, Docker, Bash (Git Bash or WSL also works on Windows), and network access are required.
git clone https://github.com/NeKiro-project/NeKiro-Stack.git
cd NeKiro-Stack
work_root=$(mktemp -d)
backend_env="$work_root/backend.env"
prepared_env="$work_root/prepared.env"
./scripts/write-ci-env.sh backend "$backend_env" "$(pwd)" nekiro-quickstart
set -a
source "$backend_env"
set +a
./scripts/prepare.sh "$(pwd)/components.json" "$work_root/checkouts" "$prepared_env"
set -a
source "$prepared_env"
set +a
go run ./cmd/nacos-secure-fixture generate "$NEKIRO_E2E_TLS_ROOT"
docker compose --project-name "$NEKIRO_E2E_COMPOSE_PROJECT" \
--file compose.yaml \
--file "$NEKIRO_E2E_COMPOSE_OVERRIDE_FILE" \
--profile router-nacos-secure \
up --detach --wait --wait-timeout 120
go test -tags=e2e -run '^TestInvokeToRecordAcceptance$' -count=1 ./tests/backend
docker compose --project-name "$NEKIRO_E2E_COMPOSE_PROJECT" \
--file compose.yaml \
--file "$NEKIRO_E2E_COMPOSE_OVERRIDE_FILE" \
--profile router-nacos-secure \
--profile runtime-registration \
--profile watch-refresh \
down --volumes --remove-orphansThe final test proves all of the following in one run:
| Check | Evidence |
|---|---|
| Register | Runtime A publishes a ready, exact-Release instance lease to Nacos. |
| Discover | Router reads the initial snapshot and watches the instance lifecycle. |
| Invoke | Runtime B reaches A by Agent ID and capability through Router only. |
| Nested invoke | A -> B and B -> A both re-enter Router; neither runtime receives the other's address. |
| Record | Ledger contains correlated parent/child metadata with root_task_id and trace_id. |
| Recovery | Removal fails closed and a replacement instance becomes routable. |
Runtime A --lease--> Nacos --snapshot/watch--> Router
Runtime B --Agent ID + capability--> Router --> Runtime A
|
+--> Invocation Ledger
Runtime A <---- managed A2A ----> Router <---- managed A2A ----> Runtime B
Consumers never resolve or dial a Provider address. Core owns the registration,
heartbeat, lease, and deregistration semantics through
registry and
registry/nacos.
The public SDK
agent/registration/nacos
maps explicit RUNTIME_A_* / RUNTIME_B_* settings, builds the secured HTTP
transport, and connects the Core lease to the managed host lifecycle. Samples
still owns each Agent's configuration, Router authentication, handlers, and
endpoint ownership challenge. External providers can import the SDK
registration package directly; the Samples internal/challengeproof package
remains a sample-owned deployment detail and is not a public API.
The complete production sources are Runtime A main, Runtime B main, and B -> A nested invocation.
The programs below are complete package main entrypoints. They keep Runtime
configuration, Router authentication, challenge proof, and handlers in Samples.
The public agent/registration/nacos
package owns strict registration composition, while
agent/host
owns serving, lease observation, bounded shutdown, and deregistration.
Runtime A: register, serve, watch the lease, and deregister
package main
import (
"context"
"log"
"net/http"
"os"
"syscall"
"time"
"github.com/NeKiro-project/NeKiro-Samples/internal/challengeproof"
runtimea "github.com/NeKiro-project/NeKiro-Samples/runtime-a"
agenthost "github.com/NeKiro-project/nekiro-sdk-go/agent/host"
registrationnacos "github.com/NeKiro-project/nekiro-sdk-go/agent/registration/nacos"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
return runWithLookup(os.LookupEnv)
}
func runWithLookup(lookup func(string) (string, bool)) error {
config, err := runtimea.LoadConfig(lookup)
if err != nil {
return agenthost.Wrap(agenthost.StageConfig, "load Runtime A configuration", err)
}
registrationConfig, err := registrationnacos.LoadConfig(lookup, "RUNTIME_A", config.AgentID, config.InstanceID)
if err != nil {
return agenthost.Wrap(agenthost.StageConfig, "load Runtime A registration configuration", err)
}
registration, readiness, err := newRuntimeRegistration(registrationConfig)
if err != nil {
return agenthost.Wrap(agenthost.StageRegistration, "create Runtime A registration", err)
}
handler, err := runtimea.NewHandler(config, http.DefaultClient)
if err != nil {
return agenthost.Wrap(agenthost.StageHandler, "create Runtime A handler", err)
}
application, err := challengeproof.NewHandler(runtimea.NewHTTPHandlerWithReadiness(handler, readiness), lookup)
if err != nil {
return agenthost.Wrap(agenthost.StageHandler, "configure Runtime A endpoint challenge", err)
}
shutdownTimeout := 5 * time.Second
if registrationConfig.RequestTimeout > 0 {
shutdownTimeout = registrationConfig.RequestTimeout
}
runtimeHost, err := agenthost.New(agenthost.Config{
Address: config.ListenAddress,
Handler: application,
Registration: registration,
ShutdownTimeout: shutdownTimeout,
Signals: []os.Signal{os.Interrupt, syscall.SIGTERM},
})
if err != nil {
return err
}
return runtimeHost.Run(context.Background())
}
type ready bool
func (value ready) Ready() bool { return bool(value) }
func newRuntimeRegistration(config registrationnacos.Config) (agenthost.Registration, runtimea.Readiness, error) {
if config.Mode == registrationnacos.ModeDisabled {
return nil, ready(true), nil
}
registration, err := registrationnacos.New(config)
if err != nil {
return nil, nil, err
}
return registration, registration, nil
}Runtime B: authenticate Router calls and invoke A by capability
package main
import (
"context"
"log"
"net/http"
"os"
"syscall"
"time"
"github.com/NeKiro-project/NeKiro-Samples/internal/challengeproof"
runtimeb "github.com/NeKiro-project/NeKiro-Samples/runtime-b"
agenthost "github.com/NeKiro-project/nekiro-sdk-go/agent/host"
registrationnacos "github.com/NeKiro-project/nekiro-sdk-go/agent/registration/nacos"
"github.com/NeKiro-project/nekiro-sdk-go/agent/routerauth"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
return runWithLookup(os.LookupEnv)
}
func runWithLookup(lookup func(string) (string, bool)) error {
address, err := runtimeb.ListenAddressFromEnvironment(lookup)
if err != nil {
return agenthost.Wrap(agenthost.StageConfig, "load Runtime B listen address", err)
}
authenticationConfig, err := routerauth.LoadConfig(lookup)
if err != nil {
return agenthost.Wrap(agenthost.StageConfig, "load Runtime B authentication configuration", err)
}
config, err := runtimeb.LoadConfig(lookup)
if err != nil {
return agenthost.Wrap(agenthost.StageConfig, "load Runtime B configuration", err)
}
registrationConfig, err := registrationnacos.LoadConfig(lookup, "RUNTIME_B", config.AgentID, config.InstanceID)
if err != nil {
return agenthost.Wrap(agenthost.StageConfig, "load Runtime B registration configuration", err)
}
registration, readiness, err := newRuntimeRegistration(registrationConfig)
if err != nil {
return agenthost.Wrap(agenthost.StageRegistration, "create Runtime B registration", err)
}
handler, err := runtimeb.NewConfiguredHandler(config, http.DefaultClient)
if err != nil {
return agenthost.Wrap(agenthost.StageHandler, "create Runtime B handler", err)
}
execution, err := runtimeb.NewHTTPHandlerWithAuthAndReadiness(handler, authenticationConfig, readiness)
if err != nil {
return agenthost.Wrap(agenthost.StageHandler, "configure Runtime B authentication", err)
}
application, err := challengeproof.NewHandler(execution, lookup)
if err != nil {
return agenthost.Wrap(agenthost.StageHandler, "configure Runtime B endpoint challenge", err)
}
shutdownTimeout := 5 * time.Second
if registrationConfig.RequestTimeout > 0 {
shutdownTimeout = registrationConfig.RequestTimeout
}
runtimeHost, err := agenthost.New(agenthost.Config{
Address: address,
Handler: application,
Registration: registration,
ShutdownTimeout: shutdownTimeout,
Signals: []os.Signal{os.Interrupt, syscall.SIGTERM},
})
if err != nil {
return err
}
return runtimeHost.Run(context.Background())
}
type ready bool
func (value ready) Ready() bool { return bool(value) }
func newRuntimeRegistration(config registrationnacos.Config) (agenthost.Registration, runtimeb.Readiness, error) {
if config.Mode == registrationnacos.ModeDisabled {
return nil, ready(true), nil
}
registration, err := registrationnacos.New(config)
if err != nil {
return nil, nil, err
}
return registration, registration, nil
}B verifies the Router-issued credential before extracting PlatformContext.
The nested handler then calls the public Agent SDK with
TargetAgentID: "runtime-a"; it never reads a Nacos endpoint or dials A
directly.
For Core development, Go 1.26 or newer is required:
git clone https://github.com/NeKiro-project/NeKiro.git
cd NeKiro
go mod download
go build ./...
go test ./...
go test -race ./...
go vet ./...Build the Core service images from the repository root:
docker build --file apps/control-plane/Dockerfile --tag nekiro-control-plane:local .
docker build --file apps/a2a-router/Dockerfile --tag nekiro-a2a-router:local .Running the binaries alone is a Core development check, not product E2E success. Use NeKiro-Stack to validate registration, discovery, installation, routing, sample Agents, Console behavior, and committed Ledger records together.
NeKiro uses separate repositories to keep ownership and release boundaries explicit.
| Repository | Responsibility |
|---|---|
| NeKiro | Control Plane, A2A Router, contracts, Config Center source semantics, service-owned migrations, and Core verification. |
| NeKiro-Console | Production web Console. |
| nekiro-sdk-go | Public Go SDKs for applications and Agents. |
| NeKiro-Samples | Cross-runtime sample Agents and Provider deployment wiring. |
| NeKiro-Stack | Immutable multi-component assembly and product backend/browser E2E. |
| nekiro-a2a-transport-go | Reusable A2A HTTP/JSON-RPC/SSE transport mechanics. |
Core required CI does not check out or build satellite source. A separate post-merge Satellite Integration workflow invokes satellite-owned reusable workflows against the exact merged Core SHA.
apps/control-plane/ Gateway, Catalog, Workspace, publication, and dispatch
apps/a2a-router/ A2A routing, credentials, topology, policy, and Ledger
config_center/ Provider-neutral byte snapshots, reads, watches, publish
contracts/ JSON Schema, OpenAPI, A2A profiles, and Go mappings
tests/ Core contract and service integration verification
docs/ Architecture, contracts, ADRs, usage, and operations
Catalog, Workspace, and Ledger migrations stay beside the modules that own their schemas and are embedded in the corresponding service binaries.
- No guessed defaults for secrets, production endpoints, database addresses, JWT keys, certificates, or trust roots.
- No Consumer-to-Provider direct path in managed invocation.
- No system-root fallback or
InsecureSkipVerifyfor secured Nacos modes. - No automatic retry, alternate endpoint, old component, or stale topology success.
- No Agent payloads, credentials, or keys in Ledger metadata.
- No shared internal implementation types across deployment boundaries; cross-process data uses versioned contracts.
Security and compatibility decisions are documented as ADRs under
docs/decisions, including runtime trust, signed Router
credentials, instance discovery, registration leases, and Nacos transport
security.
PostgreSQL integration suites require an explicit dedicated database whose
name ends in _test:
export NEKIRO_TEST_DATABASE_URL='postgresql://user:password@127.0.0.1:5432/nekiro_core_test?sslmode=disable'
go test -tags=integration -count=1 ./apps/control-plane/internal/catalog/postgres
go test -tags=integration -count=1 ./apps/control-plane/internal/workspace/postgres
go test -tags=integration -count=1 ./apps/control-plane/internal/workspace/integration
go test -tags=integration -count=1 ./apps/a2a-router/internal/ledger
go test -tags=integration -count=1 ./tests/integration/catalogUseful documentation:
- Core development
- Trusted publication operations
- Config Center runtime operations
- External Gateway operations
- Contract compatibility policy
- Central RepoWiki
NeKiro started from the idea of “pluggable Agents” and has evolved into an Agent Framework focused on the links and contracts between independently implemented nodes. The current direction is to make the complete managed Provider/Consumer lifecycle increasingly deployable, secure, observable, and language neutral while leaving Agent-internal intelligence to runtime owners.
Issues and pull requests are welcome. Changes to public behavior, contracts, data ownership, or architecture should begin with an Issue; ownership or compatibility decisions should also update an ADR. See Core development for the expected validation commands.
The annotated tag pre-repository-split-2026-08-04 preserves the accepted
monorepo tree and tracked Spec Kit history. Repository ownership and migration
rationale are recorded in
ADR 0009.
NeKiro is licensed under the Apache License 2.0.