# Attach/detach a reusable LLM config to an agent, and optionally change the Source: https://docs.nasiko.com/api-reference/agents/attachdetach-a-reusable-llm-config-to-an-agent-and-optionally-change-the /api-reference/openapi.json patch /api/agents/{id}/llm-config Attach/detach a reusable LLM config to an agent, and optionally change the inbound SDK format. A config can only be attached if it belongs to the agent owner. Owner-or-superuser only. # Detach the config and clear the agent-level pin in one call. Owner-or-superuser only. Source: https://docs.nasiko.com/api-reference/agents/detach-the-config-and-clear-the-agent-level-pin-in-one-call-owner-or-superuser-only /api-reference/openapi.json delete /api/agents/{id}/llm-config # Get an agent's current (non-stopped) deployment. Callers who can't deploy Source: https://docs.nasiko.com/api-reference/agents/get-an-agents-current-non-stopped-deployment-callers-who-cant-deploy /api-reference/openapi.json get /api/agents/{id}/deployment Get an agent's current (non-stopped) deployment. Callers who can't deploy or can't access the agent get `200 {"available": false}` instead of an error. # Get the status of a single upload/build (by `upload_id`, i.e. the build id Source: https://docs.nasiko.com/api-reference/agents/get-the-status-of-a-single-uploadbuild-by-`upload_id`-ie-the-build-id /api-reference/openapi.json get /api/agents/uploads/{upload_id} Get the status of a single upload/build (by `upload_id`, i.e. the build id as a string). Callers below deployer role, or a non-owner non-superuser, get `200 {"available": false}` instead of an error. # Issue a short-lived agent identity token for a local SDK process. The caller Source: https://docs.nasiko.com/api-reference/agents/issue-a-short-lived-agent-identity-token-for-a-local-sdk-process-the-caller /api-reference/openapi.json post /api/agents/{id}/llm-token Issue a short-lived agent identity token for a local SDK process. The caller must own the agent; the signing secret never leaves the control plane. # List agents the caller has uploaded (superuser → all), one row per agent Source: https://docs.nasiko.com/api-reference/agents/list-agents-the-caller-has-uploaded-superuser-→-all-one-row-per-agent /api-reference/openapi.json get /api/agents/my-uploads List agents the caller has uploaded (superuser → all), one row per agent (most recent upload), joined with live catalog metadata. # List the caller's deployments (superuser → all, newest 50 first). Callers Source: https://docs.nasiko.com/api-reference/agents/list-the-callers-deployments-superuser-→-all-newest-50-first-callers /api-reference/openapi.json get /api/agents/deployments List the caller's deployments (superuser → all, newest 50 first). Callers below deployer role get `200 {"available": false}` instead of an error — see `nasiko_server::unavailable`. # List the caller's uploads/builds, newest first (superuser → all). Also Source: https://docs.nasiko.com/api-reference/agents/list-the-callers-uploadsbuilds-newest-first-superuser-→-all-also /api-reference/openapi.json get /api/agents/uploads List the caller's uploads/builds, newest first (superuser → all). Also mounted at the top-level `/api/upload-status` alias (`status_router`). Callers below deployer role get `200 {"available": false}` instead of an error. # Rebuild and redeploy an agent from a new source archive, bumping its Source: https://docs.nasiko.com/api-reference/agents/rebuild-and-redeploy-an-agent-from-a-new-source-archive-bumping-its /api-reference/openapi.json put /api/agents/{id}/update Rebuild and redeploy an agent from a new source archive, bumping its version. Runs the build asynchronously (poll via `/uploads/{id}` SSE or `/deploys/{build_id}/stream`). Owner-or-superuser only. # Register a new agent from a source zip and queue an asynchronous Source: https://docs.nasiko.com/api-reference/agents/register-a-new-agent-from-a-source-zip-and-queue-an-asynchronous /api-reference/openapi.json post /api/agents/upload Register a new agent from a source zip and queue an asynchronous build-and-deploy job (poll via `/uploads/{upload_id}` or `/deploys/{build_id}/stream`). Deployer role required. # Restart an agent's deployment (K8s: scale-to-1; Docker: destroy + recreate). Source: https://docs.nasiko.com/api-reference/agents/restart-an-agents-deployment-k8s:-scale-to-1;-docker:-destroy-+-recreate /api-reference/openapi.json post /api/agents/deployment/{deployment_id}/restart Restart an agent's deployment (K8s: scale-to-1; Docker: destroy + recreate). Owner-or-superuser only. # Roll back to a previous rollback-eligible version (defaults to the most Source: https://docs.nasiko.com/api-reference/agents/roll-back-to-a-previous-rollback-eligible-version-defaults-to-the-most /api-reference/openapi.json post /api/agents/{id}/rollback Roll back to a previous rollback-eligible version (defaults to the most recent one) — a synthetic build record so the same SSE polling path works, no image rebuild. Owner-or-superuser only. # Server-Sent Events stream of `{"status": ..., "build_id": ...}` on each Source: https://docs.nasiko.com/api-reference/agents/server-sent-events-stream-of-`-`-on-each /api-reference/openapi.json get /api/agents/deploys/{build_id}/stream Server-Sent Events stream of `{"status": ..., "build_id": ...}` on each status change, polling every 3s until the build reaches `success`/`failed` (or `{"status": "not_found"}` once, if the build id doesn't exist). Callers below deployer role get `200 {"available": false}` instead. # The agent's **resolved** routing config (attached → owner default → Source: https://docs.nasiko.com/api-reference/agents/the-agents-**resolved**-routing-config-attached-→-owner-default-→ /api-reference/openapi.json get /api/agents/{id}/llm-config The agent's **resolved** routing config (attached → owner default → none), which config is attached, its source, and the inbound format. Owner-or-superuser only. # The platform fallback env vars the CP injects into every deployment Source: https://docs.nasiko.com/api-reference/agents/the-platform-fallback-env-vars-the-cp-injects-into-every-deployment /api-reference/openapi.json get /api/agents/dev-env The platform fallback env vars the CP injects into every deployment (OPENAI_* today). Consumed by `nasiko run` so a local container starts with the same defaults a CP deployment would get. Deployer-gated: anyone who can deploy already receives these values inside the containers they deploy. # Approvals API Source: https://docs.nasiko.com/api-reference/approvals Human-in-the-loop request lifecycle. Not in the generated OpenAPI spec. Behavioral guide: [Approvals](/governance/approvals). | Method | Path | Who | | - | - | - | | `GET` | `/api/hitl/pending` | Owner | | `GET` | `/api/hitl/{id}` | Owner | | `POST` | `/api/hitl/{id}/resolve` | Owner | | `POST` | `/api/hitl/{id}/cancel` | Owner | | `GET` | `/api/hitl/{id}/stream` | Owner (SSE) | | `POST` | `/api/hitl/{id}/requeue` | Superuser | Resolve body fields depend on kind (`tool_approval`, `input_required`, `auth_required`) — see the guide. # Clone a GitHub repo (via the caller's stored OAuth token), then build and deploy it. Source: https://docs.nasiko.com/api-reference/catalog/clone-a-github-repo-via-the-callers-stored-oauth-token-then-build-and-deploy-it /api-reference/openapi.json post /api/import/github # Copy a subset of the caller's own user-scoped secrets into an agent's Source: https://docs.nasiko.com/api-reference/catalog/copy-a-subset-of-the-callers-own-user-scoped-secrets-into-an-agents /api-reference/openapi.json post /api/agents/{agent_id}/secrets/import Copy a subset of the caller's own user-scoped secrets into an agent's secrets_env, re-encrypting them under the agent's key. Owner-or-superuser only. # Create or overwrite one of an agent's secrets. Owner-or-superuser only. Source: https://docs.nasiko.com/api-reference/catalog/create-or-overwrite-one-of-an-agents-secrets-owner-or-superuser-only /api-reference/openapi.json post /api/agents/{agent_id}/secrets # Delete a specific version record. Rejects deleting the currently active Source: https://docs.nasiko.com/api-reference/catalog/delete-a-specific-version-record-rejects-deleting-the-currently-active /api-reference/openapi.json delete /api/agents/{id}/versions/{version} Delete a specific version record. Rejects deleting the currently active version — roll back to another version first. Owner-or-superuser only. # Delete an agent and tear down its running containers (best-effort). Source: https://docs.nasiko.com/api-reference/catalog/delete-an-agent-and-tear-down-its-running-containers-best-effort /api-reference/openapi.json delete /api/agents/{id} Delete an agent and tear down its running containers (best-effort). Owner-or-superuser only. # Delete one of an agent's secrets. Owner-or-superuser only. Source: https://docs.nasiko.com/api-reference/catalog/delete-one-of-an-agents-secrets-owner-or-superuser-only /api-reference/openapi.json delete /api/agents/{agent_id}/secrets/{name} # Discover agents that have a skill tagged `tag`. Access-scoped like `list` Source: https://docs.nasiko.com/api-reference/catalog/discover-agents-that-have-a-skill-tagged-`tag`-access-scoped-like-`list` /api-reference/openapi.json get /api/agents/by-skill Discover agents that have a skill tagged `tag`. Access-scoped like `list` (superuser → all; otherwise owner ∪ public ∪ user-grant — see `agent_access_predicate`). Uses the GIN `idx_agent_skills_tags` via the `@>` containment operator and `EXISTS` (no join fan-out / DISTINCT). # Fetch a single agent by UUID or name, rendered as an A2A AgentCard-shaped envelope. Source: https://docs.nasiko.com/api-reference/catalog/fetch-a-single-agent-by-uuid-or-name-rendered-as-an-a2a-agentcard-shaped-envelope /api-reference/openapi.json get /api/agents/{id} # Import an agent from an OCI registry: a source-tarball layer is built and Source: https://docs.nasiko.com/api-reference/catalog/import-an-agent-from-an-oci-registry:-a-source-tarball-layer-is-built-and /api-reference/openapi.json post /api/import/registry Import an agent from an OCI registry: a source-tarball layer is built and deployed like `/import/upload`; a plain container image is pulled and deployed directly. # List agents visible to the caller (superuser → all; otherwise owner ∪ Source: https://docs.nasiko.com/api-reference/catalog/list-agents-visible-to-the-caller-superuser-→-all;-otherwise-owner-∪ /api-reference/openapi.json get /api/agents List agents visible to the caller (superuser → all; otherwise owner ∪ public ∪ user-grant — see `agent_access_predicate`). # List an agent's build/version history. Source: https://docs.nasiko.com/api-reference/catalog/list-an-agents-buildversion-history /api-reference/openapi.json get /api/agents/{id}/versions # List an agent's secret names (never decrypted values). Owner-or-superuser only. Source: https://docs.nasiko.com/api-reference/catalog/list-an-agents-secret-names-never-decrypted-values-owner-or-superuser-only /api-reference/openapi.json get /api/agents/{agent_id}/secrets # Look up an agent by its UUID or name (registry-entry lookup). Source: https://docs.nasiko.com/api-reference/catalog/look-up-an-agent-by-its-uuid-or-name-registry-entry-lookup /api-reference/openapi.json get /api/registries/{id} Look up an agent by its UUID or name (registry-entry lookup). Equivalent to GET /agents/{id} but exposed at the /registries/ path for clients that use the registry-centric URL scheme. # Post apiagentscoding integrations Source: https://docs.nasiko.com/api-reference/catalog/post-apiagentscoding-integrations /api-reference/openapi.json post /api/agents/coding-integrations # Register a new agent in the catalog. Source: https://docs.nasiko.com/api-reference/catalog/register-a-new-agent-in-the-catalog /api-reference/openapi.json post /api/agents # Returns agents accessible to the current user: owned + public + explicitly granted. Source: https://docs.nasiko.com/api-reference/catalog/returns-agents-accessible-to-the-current-user:-owned-+-public-+-explicitly-granted /api-reference/openapi.json get /api/registry/user/agents Returns agents accessible to the current user: owned + public + explicitly granted. Supports optional `?q` (name/description search), `?status`, `?limit`, `?offset`. Superusers see all agents. # search Source: https://docs.nasiko.com/api-reference/catalog/search /api-reference/openapi.json get /api/search/agents Agent-only search. Scoring: GREATEST across (name×2.8, description×2.0, tag score) with tiered exact/prefix/contains scoring. Minimum query length: 2 chars. # Search the user directory. The user directory (usernames + emails) is Source: https://docs.nasiko.com/api-reference/catalog/search-the-user-directory-the-user-directory-usernames-+-emails-is /api-reference/openapi.json get /api/search/users Search the user directory. The user directory (usernames + emails) is sensitive (CAT-4), so results are scoped via `AuthService::org_visible_user_ids` — OSS returns `None` (unrestricted, no org hierarchy to scope by); EE restricts non-admin callers to their own department/team, same as the MCP share-target picker (`oss/mcp-gateway`'s `search_share_targets_view`). An exact username/display-name match bypasses that scope, mirroring `resolve_share_target`/`departments.rs::resolve`/`teams.rs::resolve` — a caller who already knows exactly who they're looking for (e.g. a connector owner sharing outside their own team) can still find them by typing the full name, they just can't browse/enumerate people outside their scope via a partial query. # Update an agent's catalog metadata. Owner-or-superuser only. Source: https://docs.nasiko.com/api-reference/catalog/update-an-agents-catalog-metadata-owner-or-superuser-only /api-reference/openapi.json put /api/agents/{id} # Upload a source archive and synchronously build + deploy it as an agent. Source: https://docs.nasiko.com/api-reference/catalog/upload-a-source-archive-and-synchronously-build-+-deploy-it-as-an-agent /api-reference/openapi.json post /api/import/upload Meant to be quick and direct, not production-grade robust: unlike its sibling `/api/agents/upload` (`oss/server/src/agents/upload.rs`), which is asynchronous, tracked via `upload_status`, and retried on failure through a real job queue, this runs the whole build-and-deploy pipeline synchronously inside the request handler, with no progress tracking and no retry — you get a response once the build either finishes or fails, and that's it. # Coding agents API Source: https://docs.nasiko.com/api-reference/coding-agents Register a harness identity and ingest session-turn telemetry. `POST /api/agents/coding-integrations` is in the generated OpenAPI spec. Batch ingest is not. | Method | Path | Auth | Purpose | | - | - | - | - | | `POST` | `/api/agents/coding-integrations` | Session JWT | Register Claude / Codex / OpenCode / Cursor for this user. Idempotent. Body `{ "integration_id": "claude" }` (or `codex`, `opencode`, `cursor`) | | `POST` | `/api/telemetry/coding-agent/events/batch` | Session JWT bound to the install | Up to 100 queued turns. Replay is safe | The CLI is what calls these (`nasiko agents install`, `nasiko agents sync`). Don't send turns from a different user or cluster than the install binding — `sync` refuses that locally; the server still requires a valid login. Related: [Discover](/coding-agents/discover), [Session reporting](/coding-agents/session-reporting). # LLM router API Source: https://docs.nasiko.com/api-reference/llm-router OpenAI-, Anthropic-, and Gemini-compatible endpoints the Nasiko LLM router serves. Not in the generated OpenAPI spec. Agents and coding harnesses call these on the Nasiko server, not on the vendor. Auth: agent JWT (`Authorization: Bearer` or `x-api-key`). User/session: W3C `traceparent` whose trace ID is a live flow. Missing or unresolvable `traceparent` → **403**. Empty `AGENT_JWT_SECRET` → **401**. See [Providers](/models/providers) and [Cost attribution](/tokenops/attribution). | Method | Path | Protocol | | - | - | - | | `GET` | `/v1/health` | Liveness, no auth | | `POST` | `/v1/chat/completions` | OpenAI Chat Completions | | `POST` | `/v1/responses` | OpenAI Responses (Codex) | | `POST` | `/v1/messages` | Anthropic Messages (Claude Code) | | `POST` | `/v1beta/models/{model_method}` | Gemini generate / stream | | `POST` | `/v1/embeddings` | Embeddings (same attribution rule) | | `GET` | `/v1/models` | List models | Library management (in the generated spec): `/api/llm-configs`, `/api/agents/{id}/llm-config`, `/api/agents/{id}/llm-token`, `/api/llm-router/providers`, `/api/model-registry`, `/api/custom-providers`. # Clear the default flag on one of the caller's configs, leaving them with no Source: https://docs.nasiko.com/api-reference/llm-router/clear-the-default-flag-on-one-of-the-callers-configs-leaving-them-with-no /api-reference/openapi.json delete /api/llm-configs/{id}/default Agents that were falling back to this default resolve to `source: "none"` afterwards and use the platform key path (see `resolve_agent_config`). # Create a named LLM config owned by the caller, optionally storing the Source: https://docs.nasiko.com/api-reference/llm-router/create-a-named-llm-config-owned-by-the-caller-optionally-storing-the /api-reference/openapi.json post /api/llm-configs Create a named LLM config owned by the caller, optionally storing the referenced API-key secret and/or marking it as the caller's default. # Fetch one of the caller's configs; configs the caller doesn't own read as 404. Source: https://docs.nasiko.com/api-reference/llm-router/fetch-one-of-the-callers-configs;-configs-the-caller-doesnt-own-read-as-404 /api-reference/openapi.json get /api/llm-configs/{id} # List every configured tier→model mapping, ordered by provider and tier. Source: https://docs.nasiko.com/api-reference/llm-router/list-every-configured-tier→model-mapping-ordered-by-provider-and-tier /api-reference/openapi.json get /api/model-registry # List every provider/model with a currently-effective `model_pricing` row, Source: https://docs.nasiko.com/api-reference/llm-router/list-every-providermodel-with-a-currently-effective-`model_pricing`-row /api-reference/openapi.json get /api/llm-router/providers List every provider/model with a currently-effective `model_pricing` row, grouped by provider. Backs the UI provider/model dropdown. # List the caller's LLM configs, ordered by name. Source: https://docs.nasiko.com/api-reference/llm-router/list-the-callers-llm-configs-ordered-by-name /api-reference/openapi.json get /api/llm-configs # Mark one of the caller's configs as their default, clearing any prior default. Source: https://docs.nasiko.com/api-reference/llm-router/mark-one-of-the-callers-configs-as-their-default-clearing-any-prior-default /api-reference/openapi.json post /api/llm-configs/{id}/default # Partially update one of the caller's configs; absent fields keep their Source: https://docs.nasiko.com/api-reference/llm-router/partially-update-one-of-the-callers-configs;-absent-fields-keep-their /api-reference/openapi.json patch /api/llm-configs/{id} Partially update one of the caller's configs; absent fields keep their current value. Use `POST /api/llm-configs/{id}/default` to change the default. # Soft-delete one of the caller's configs. Fails while any live agent still Source: https://docs.nasiko.com/api-reference/llm-router/soft-delete-one-of-the-callers-configs-fails-while-any-live-agent-still /api-reference/openapi.json delete /api/llm-configs/{id} Soft-delete one of the caller's configs. Fails while any live agent still has the config attached. # Upsert one `(provider, tier)` → model mapping. Superuser only. Source: https://docs.nasiko.com/api-reference/llm-router/upsert-one-`provider-tier`-→-model-mapping-superuser-only /api-reference/openapi.json put /api/model-registry # `DELETE /api/mcp/agents/{agent_id}/permissions` — reset to all-allowed. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpagents-permissions`-—-reset-to-all-allowed /api-reference/openapi.json delete /api/mcp/agents/{agent_id}/permissions # `DELETE /api/mcp/auth-configs/{connector_id}` — remove a composio connector (admin). Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpauth-configs-`-—-remove-a-composio-connector-admin /api-reference/openapi.json delete /api/mcp/auth-configs/{connector_id} # `DELETE /api/mcp/connections/{connector_id}` — disconnect the caller's connection. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnections-`-—-disconnect-the-callers-connection /api-reference/openapi.json delete /api/mcp/connections/{connector_id} # `DELETE /api/mcp/connectors/{id}` — delete an owned connector (or any, if admin). Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-`-—-delete-an-owned-connector-or-any-if-admin /api-reference/openapi.json delete /api/mcp/connectors/{id} # `DELETE /api/mcp/connectors/{id}/credential` — remove the caller's credential. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-credential`-—-remove-the-callers-credential /api-reference/openapi.json delete /api/mcp/connectors/{id}/credential # `DELETE /api/mcp/connectors/{id}/grants/agents/{agent_id}` — revoke from an agent. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-grantsagents-`-—-revoke-from-an-agent /api-reference/openapi.json delete /api/mcp/connectors/{id}/grants/agents/{agent_id} # `DELETE /api/mcp/connectors/{id}/grants/public` — revoke public access. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-grantspublic`-—-revoke-public-access /api-reference/openapi.json delete /api/mcp/connectors/{id}/grants/public # `DELETE /api/mcp/connectors/{id}/grants/users/{user_id}` — revoke from a user. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-grantsusers-`-—-revoke-from-a-user /api-reference/openapi.json delete /api/mcp/connectors/{id}/grants/users/{user_id} # `DELETE /api/mcp/connectors/{id}/oauth/token` — remove the caller's token. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-oauthtoken`-—-remove-the-callers-token /api-reference/openapi.json delete /api/mcp/connectors/{id}/oauth/token # `DELETE /api/mcp/connectors/{id}/pin` — unpin. Source: https://docs.nasiko.com/api-reference/mcp/`delete-apimcpconnectors-pin`-—-unpin /api-reference/openapi.json delete /api/mcp/connectors/{id}/pin # `GET /api/mcp/agents/{agent_id}/connectors/{connector_id}/tools` — tools + stances. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpagents-connectors-tools`-—-tools-+-stances /api-reference/openapi.json get /api/mcp/agents/{agent_id}/connectors/{connector_id}/tools Same relaxed gate as `set_connector_access`: the connector's own owner can view its tools/stances on this agent once it's been granted here. # `GET /api/mcp/agents/{agent_id}/connectors` — connectors + per-agent status. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpagents-connectors`-—-connectors-+-per-agent-status /api-reference/openapi.json get /api/mcp/agents/{agent_id}/connectors Same relaxed gate as `list_tool_rules` below (and `set_connector_access`/ `list_connector_tools`, already relaxed by `a9012ded`/`56e46b07`): a caller who can't manage the whole agent still sees the connector(s) they themselves can reach (narrowed, not blocked outright) — otherwise this endpoint (the natural first call before `agent-tools enable`/`set-rule`) would be the one sibling still hard-denying the exact caller those other endpoints already permit, making them undiscoverable in practice. This works with zero relationship to the agent itself, e.g. a connector owner whose connector was granted to someone else's agent. If that per-connector filter finds nothing at all (no connector the caller can manage happens to be attached here), it used to hard-403 unconditionally — but an empty result is also the normal, legitimate state for a caller who has ordinary access to the agent ([`can_access_agent`] — owner, public, or a plain share) and simply hasn't attached any connector of their own yet (e.g. they're about to attach their first one). Only a caller with NEITHER a manageable connector already here NOR any relationship to the agent at all still gets the 403. # `GET /api/mcp/agents/{agent_id}/tools` — the agent's current tool rules. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpagents-tools`-—-the-agents-current-tool-rules /api-reference/openapi.json get /api/mcp/agents/{agent_id}/tools Agent-wide (every connector's rules at once), so a non-agent-manager never gets the unfiltered view: if they can't manage the whole agent, the result is narrowed to only the connector(s) they themselves can manage (`can_manage_agent_connector` — reachable, and either already granted to this agent or the caller has at least ordinary access to it), e.g. so `agent-tools set-rule`'s read-modify-write can find that connector's existing rules. This works with zero relationship to the agent itself, e.g. a connector owner whose connector was granted to someone else's agent. If that filter finds no manageable connector at all, an empty result is still a normal 200 as long as the caller has at least ordinary access to the agent ([`can_access_agent`] — owner, public, or a plain share): a freshly shared agent with no rules configured yet is not a permission failure. Only a caller with neither a manageable connector already here nor any relationship to the agent at all gets the 403. # `GET /api/mcp/auth-configs` — list platform Composio connectors (admin). Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpauth-configs`-—-list-platform-composio-connectors-admin /api-reference/openapi.json get /api/mcp/auth-configs # `GET /api/mcp/catalog` — connectable services, credential-free. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpcatalog`-—-connectable-services-credential-free /api-reference/openapi.json get /api/mcp/catalog # `GET /api/mcp/composio/toolkits` — platform Composio toolkits only. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpcomposiotoolkits`-—-platform-composio-toolkits-only /api-reference/openapi.json get /api/mcp/composio/toolkits # `GET /api/mcp/connections` — the caller's connections. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnections`-—-the-callers-connections /api-reference/openapi.json get /api/mcp/connections # `GET /api/mcp/connectors/{id}` — a single connector, 404 if not reachable. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors-`-—-a-single-connector-404-if-not-reachable /api-reference/openapi.json get /api/mcp/connectors/{id} # `GET /api/mcp/connectors/{id}/build-status` — plain polling JSON, no SSE. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors-build-status`-—-plain-polling-json-no-sse /api-reference/openapi.json get /api/mcp/connectors/{id}/build-status # `GET /api/mcp/connectors/{id}/consumers` — agents that have this connector configured. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors-consumers`-—-agents-that-have-this-connector-configured /api-reference/openapi.json get /api/mcp/connectors/{id}/consumers # `GET /api/mcp/connectors/{id}/credential/status` — whether a credential exists. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors-credentialstatus`-—-whether-a-credential-exists /api-reference/openapi.json get /api/mcp/connectors/{id}/credential/status # `GET /api/mcp/connectors/{id}/grants` — list a connector's grants. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors-grants`-—-list-a-connectors-grants /api-reference/openapi.json get /api/mcp/connectors/{id}/grants # `GET /api/mcp/connectors/{id}/oauth/status` — token presence + expiry. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors-oauthstatus`-—-token-presence-+-expiry /api-reference/openapi.json get /api/mcp/connectors/{id}/oauth/status # `GET /api/mcp/connectors` — custom connectors visible to the caller. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectors`-—-custom-connectors-visible-to-the-caller /api-reference/openapi.json get /api/mcp/connectors # `GET /api/mcp/connectors/pinned` — the caller's pinned connectors. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectorspinned`-—-the-callers-pinned-connectors /api-reference/openapi.json get /api/mcp/connectors/pinned # `GET /api/mcp/connectors/recent` — the caller's recently-used connectors. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpconnectorsrecent`-—-the-callers-recently-used-connectors /api-reference/openapi.json get /api/mcp/connectors/recent # `GET /api/mcp/oauth/callback` — public browser redirect target (HTML, not JSON). Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpoauthcallback`-—-public-browser-redirect-target-html-not-json /api-reference/openapi.json get /api/mcp/oauth/callback # `GET /api/mcp/share-targets?q=` — search users to share a connector with. Source: https://docs.nasiko.com/api-reference/mcp/`get-apimcpshare-targets?q=`-—-search-users-to-share-a-connector-with /api-reference/openapi.json get /api/mcp/share-targets # `PATCH /api/mcp/auth-configs/{connector_id}` — edit composio catalog metadata (admin). Source: https://docs.nasiko.com/api-reference/mcp/`patch-apimcpauth-configs-`-—-edit-composio-catalog-metadata-admin /api-reference/openapi.json patch /api/mcp/auth-configs/{connector_id} # `PATCH /api/mcp/connectors/{id}` — update an owned connector. Source: https://docs.nasiko.com/api-reference/mcp/`patch-apimcpconnectors-`-—-update-an-owned-connector /api-reference/openapi.json patch /api/mcp/connectors/{id} # `POST /api/mcp/auth-configs` — register a platform Composio connector (admin). Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpauth-configs`-—-register-a-platform-composio-connector-admin /api-reference/openapi.json post /api/mcp/auth-configs # `POST /api/mcp/connect` — connect any connector type. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnect`-—-connect-any-connector-type /api-reference/openapi.json post /api/mcp/connect # `POST /api/mcp/connectors/{id}/credential` — store the caller's credential. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors-credential`-—-store-the-callers-credential /api-reference/openapi.json post /api/mcp/connectors/{id}/credential # `POST /api/mcp/connectors/{id}/grants/agents/{agent_id}` — grant to an agent. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors-grantsagents-`-—-grant-to-an-agent /api-reference/openapi.json post /api/mcp/connectors/{id}/grants/agents/{agent_id} # `POST /api/mcp/connectors/{id}/grants/public` — make connector public. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors-grantspublic`-—-make-connector-public /api-reference/openapi.json post /api/mcp/connectors/{id}/grants/public # `POST /api/mcp/connectors/{id}/grants/users/{user_id}` — grant to a user. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors-grantsusers-`-—-grant-to-a-user /api-reference/openapi.json post /api/mcp/connectors/{id}/grants/users/{user_id} # `POST /api/mcp/connectors/{id}/oauth/authorize` — start the OAuth 2.1 flow. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors-oauthauthorize`-—-start-the-oauth-21-flow /api-reference/openapi.json post /api/mcp/connectors/{id}/oauth/authorize # `POST /api/mcp/connectors/{id}/pin` — pin for quick access. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors-pin`-—-pin-for-quick-access /api-reference/openapi.json post /api/mcp/connectors/{id}/pin # `POST /api/mcp/connectors` — register a custom MCP connector (owned by caller). Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectors`-—-register-a-custom-mcp-connector-owned-by-caller /api-reference/openapi.json post /api/mcp/connectors # `POST /api/mcp/connectors/probe` — detect a server's auth type. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectorsprobe`-—-detect-a-servers-auth-type /api-reference/openapi.json post /api/mcp/connectors/probe # `POST /api/mcp/connectors/upload` — multipart zip upload. Source: https://docs.nasiko.com/api-reference/mcp/`post-apimcpconnectorsupload`-—-multipart-zip-upload /api-reference/openapi.json post /api/mcp/connectors/upload # `PUT /api/mcp/agents/{agent_id}/connectors/{connector_id}` — toggle a connector. Source: https://docs.nasiko.com/api-reference/mcp/`put-apimcpagents-connectors-`-—-toggle-a-connector /api-reference/openapi.json put /api/mcp/agents/{agent_id}/connectors/{connector_id} Allows either full agent management, or the connector's own owner acting on their connector once it's been granted to this agent (see `ensure_can_manage_agent_connector`) — a connector owner can enable/disable their own connector on someone else's agent without managing the agent itself. # `PUT /api/mcp/agents/{agent_id}/tools` — batch upsert tool rules. Source: https://docs.nasiko.com/api-reference/mcp/`put-apimcpagents-tools`-—-batch-upsert-tool-rules /api-reference/openapi.json put /api/mcp/agents/{agent_id}/tools Each rule names its own `connector_id`, so the gate is per-connector: full agent management, or (for each distinct connector referenced) that connector's own owner acting on a connector already granted to this agent — same relaxed rule as `set_connector_access`. An empty batch still requires full agent management, since there's no connector to scope a narrower check to. # Agent-facing MCP JSON-RPC gateway. Generic `tools/list` / `tools/call` Source: https://docs.nasiko.com/api-reference/mcp/agent-facing-mcp-json-rpc-gateway-generic-`toolslist`-`toolscall` /api-reference/openapi.json post /api/mcp Authorization rules (docs/MCP_GATEWAY_AGENT_AUTH.md §2.4), all failing closed: 1. bearer token missing/unknown/revoked → 401 2. `tools/list` (and initialize/ping) → allowed with agent-only identity 3. `tools/call` with no/unknown/dead-flow traceparent → 403 4. `tools/call` where the agent is not a recorded flow participant → 403 5. identity store unreachable → 403 # build logs Source: https://docs.nasiko.com/api-reference/mcp/build-logs /api-reference/openapi.json get /api/mcp/connectors/{id}/build-logs `GET /api/mcp/connectors/{id}/build-logs` — same ownership check as `build_status`, real container stdout/stderr via `ContainerRuntime::logs`. # list my uploads Source: https://docs.nasiko.com/api-reference/mcp/list-my-uploads /api-reference/openapi.json get /api/mcp/connectors/my-uploads `GET /api/mcp/connectors/my-uploads` — list uploaded MCP connectors owned by the caller, mirroring `/api/agents/my-uploads`. # resolve target Source: https://docs.nasiko.com/api-reference/mcp/resolve-target /api-reference/openapi.json get /api/mcp/share-targets/resolve `GET /api/mcp/share-targets/resolve?username=` — exact username → user_id, for `share add`/`share remove` to target someone outside the caller's own org-visibility scope (unlike `search_targets` above, this is intentionally NOT scoped — see `service::connectors::resolve_share_target`'s doc comment). # upload github Source: https://docs.nasiko.com/api-reference/mcp/upload-github /api-reference/openapi.json post /api/mcp/connectors/upload-github `POST /api/mcp/connectors/upload-github` — clone a repo instead of a zip. Re-validates `github_url` (HTTPS-only + host allowlist) the same way `execute_build`/`execute_mcp_server_build` already do at clone time — this is a defence-in-depth check at the handler layer, not the only one. # `GET /api/observe/agents/{agent_ref}/logs` Source: https://docs.nasiko.com/api-reference/observability/`get-apiobserveagents-logs` /api-reference/openapi.json get /api/observability/agents/{agent_ref}/logs `agent_ref` may be a UUID or an agent name (e.g. `my-agent`). Returns a merged, time-sorted list of log lines from up to three sources: 1. Loki (when observability backend is configured) 2. `proxy_logs` DB table (always) 3. Container stdout/stderr via the runtime (always) # `GET /api/observe/agents/{agent_ref}/logs/stream` Source: https://docs.nasiko.com/api-reference/observability/`get-apiobserveagents-logsstream` /api-reference/openapi.json get /api/observability/agents/{agent_ref}/logs/stream SSE live-tail stream. `agent_ref` may be a UUID or agent name. Protocol: • Immediately emits the last 30 container log lines as historical context. • Then polls every 3 s for new `proxy_logs` rows (since stream opened). • Sends an SSE comment `keepalive` every cycle so the connection stays alive. # Detail for one session: traces, token usage, and cost summary. Source: https://docs.nasiko.com/api-reference/observability/detail-for-one-session:-traces-token-usage-and-cost-summary /api-reference/openapi.json get /api/observability/session/{session_id} # Detail for one span: attributes, input/output content, and cost. Source: https://docs.nasiko.com/api-reference/observability/detail-for-one-span:-attributes-inputoutput-content-and-cost /api-reference/openapi.json get /api/observability/span/{trace_id}/{span_id} # Detail for one trace: full span tree with per-span token/cost attribution. Source: https://docs.nasiko.com/api-reference/observability/detail-for-one-trace:-full-span-tree-with-per-span-tokencost-attribution /api-reference/openapi.json get /api/observability/trace/{trace_id} # FinOps dashboard: per-agent cost/token rows plus fleet-wide summary. Source: https://docs.nasiko.com/api-reference/observability/finops-dashboard:-per-agent-costtoken-rows-plus-fleet-wide-summary /api-reference/openapi.json get /api/observability/finops/dashboard # List chat sessions (DB-authoritative, enriched from Tempo when available). Source: https://docs.nasiko.com/api-reference/observability/list-chat-sessions-db-authoritative-enriched-from-tempo-when-available /api-reference/openapi.json get /api/observability/session/list # LLM-generated cost insights from the caller-supplied FinOps KPI snapshot. Source: https://docs.nasiko.com/api-reference/observability/llm-generated-cost-insights-from-the-caller-supplied-finops-kpi-snapshot /api-reference/openapi.json post /api/observability/finops/insights # Trace/cost/latency stats for one agent (accepts a UUID or agent name). Source: https://docs.nasiko.com/api-reference/observability/tracecostlatency-stats-for-one-agent-accepts-a-uuid-or-agent-name /api-reference/openapi.json get /api/observability/agent/{agent_id}/stats # Windowed replica-hours per agent (billing source of truth), optionally bucketed. Source: https://docs.nasiko.com/api-reference/observability/windowed-replica-hours-per-agent-billing-source-of-truth-optionally-bucketed /api-reference/openapi.json get /api/observability/finops/agent-hours # OCI registry API Source: https://docs.nasiko.com/api-reference/oci-registry Embedded OCI Distribution v2 used by nasiko push, deploy, and agent pulls. Not in the generated OpenAPI spec. The Nasiko server serves `/v2/*` with S3-backed blobs. This is the **image** registry for deployed agents, not the Enterprise artifact catalog. Auth: OCI bearer, agent pull token, or build-push token. | Method | Path | | - | - | | `GET` | `/v2/` | | `GET` | `/v2/_catalog` | | `GET` `PUT` `DELETE` | `/v2/{owner}/{repo}/manifests/{reference}` | | `GET` | `/v2/{owner}/{repo}/referrers/{digest}` | | `GET` `HEAD` `DELETE` | `/v2/{owner}/{repo}/blobs/{digest}` | | `POST` | `/v2/{owner}/{repo}/blobs/uploads/` | | `PATCH` `PUT` `GET` `DELETE` | `/v2/{owner}/{repo}/blobs/uploads/{uuid}` | | `GET` | `/v2/{owner}/{repo}/tags/list` | Import from an external host uses `POST /api/import/registry` (in the generated spec). That host must be in `REGISTRY_IMPORT_ALLOWED_HOSTS`. See [Operate](/build/operate) and [Agent hosting](/self-hosting/agent-hosting). # `POST /api/a2a/upload` — multipart/form-data A2A dispatch entry point. Source: https://docs.nasiko.com/api-reference/orchestrator/`post-apia2aupload`-—-multipartform-data-a2a-dispatch-entry-point /api-reference/openapi.json post /api/orchestrator/a2a/upload Accepts: - `query` (text field, required) — the user's question - Any number of additional fields treated as file attachments Each file is base64-encoded and forwarded alongside the query to the orchestrator. # Aggregated agent-selection stats from the `agent_selection_stats` Source: https://docs.nasiko.com/api-reference/orchestrator/aggregated-agent-selection-stats-from-the-`agent_selection_stats` /api-reference/openapi.json get /api/orchestrator/stats Aggregated agent-selection stats from the `agent_selection_stats` materialized view (newest date, most-selected agent first; max 200 rows). # Server-side A2A dispatch endpoint. Accepts JSONRPC `message/send` or `message/stream`. Source: https://docs.nasiko.com/api-reference/orchestrator/server-side-a2a-dispatch-endpoint-accepts-jsonrpc-`messagesend`-or-`messagestream` /api-reference/openapi.json post /api/orchestrator/a2a No gateway required: the server validates the JWT and enforces authorization itself (see `require_auth`/`Claims`). This handler is the production A2A path. # API reference Source: https://docs.nasiko.com/api-reference/overview Authentication, conventions, and where each surface is documented. The Nasiko server exposes one HTTP API — the same one the CLI, dashboard, and admin tools use. Live spec (annotated routes only): [http://localhost:8080/api/openapi.json](http://localhost:8080/api/openapi.json) on a local cluster, Swagger UI at `/api/docs`. The copy in this docs site is that generated spec. Routes without `#[utoipa::path]` are documented on the pages below. ## Authentication | Surface | Credential | | - | - | | `/api/*` | `Authorization: Bearer` session JWT from `POST /api/auth/login` (or the dashboard cookie) | | `/api/mcp` JSON-RPC | Deploy-time `MCP_GATEWAY_TOKEN` (or `/api/mcp/s/{token}`) plus `traceparent` | | `/v1/*` LLM router | Agent JWT (`Authorization` or `x-api-key`) plus `traceparent` | | `/v2/*` image registry | Registry Basic or Bearer | See [access control](/governance/access-control/overview). Strict attribution: [Cost attribution](/tokenops/attribution). ## Conventions Responses are JSON objects. Errors use HTTP status plus `{ "error", "code" }`. Field names are snake\_case. IDs are UUIDs. There is no `/api/v1` prefix — evolution is additive. ## Hand-written surfaces These are **not** in the generated OpenAPI file: Chat Completions, Responses, Messages, Gemini, embeddings. Register integrations and ingest telemetry. Pending, resolve, cancel, stream, requeue. Embedded Distribution v2 for agent images. TokenOps extra routes (`finops/spend-timeseries`, `spend-calendar`, `attributions`, `/api/usage/*`) are listed in [cost attribution](/tokenops/attribution). MCP management routes are in the generated spec and in [MCP gateway](/build/mcp/overview). Custom providers: [Providers](/models/providers). # Create a secret, or overwrite the value if one with this name already exists. Source: https://docs.nasiko.com/api-reference/secrets/create-a-secret-or-overwrite-the-value-if-one-with-this-name-already-exists /api-reference/openapi.json post /api/secrets # Delete a secret. Source: https://docs.nasiko.com/api-reference/secrets/delete-a-secret /api-reference/openapi.json delete /api/secrets/{name} # Fetch and decrypt a single secret's value. Source: https://docs.nasiko.com/api-reference/secrets/fetch-and-decrypt-a-single-secrets-value /api-reference/openapi.json get /api/secrets/{name} # List the caller's secrets (names + metadata only — never decrypted values). Source: https://docs.nasiko.com/api-reference/secrets/list-the-callers-secrets-names-+-metadata-only-—-never-decrypted-values /api-reference/openapi.json get /api/secrets # Overwrite an existing secret's value. Source: https://docs.nasiko.com/api-reference/secrets/overwrite-an-existing-secrets-value /api-reference/openapi.json put /api/secrets/{name} # Aggregate token usage and cost for the caller over the last `days` days. Source: https://docs.nasiko.com/api-reference/usage/aggregate-token-usage-and-cost-for-the-caller-over-the-last-`days`-days /api-reference/openapi.json get /api/usage/summary # Per-agent usage breakdown for the caller, ordered by total tokens. Source: https://docs.nasiko.com/api-reference/usage/per-agent-usage-breakdown-for-the-caller-ordered-by-total-tokens /api-reference/openapi.json get /api/usage/by-agent # Per-day usage breakdown for the caller over the last `days` days. Source: https://docs.nasiko.com/api-reference/usage/per-day-usage-breakdown-for-the-caller-over-the-last-`days`-days /api-reference/openapi.json get /api/usage/history # Per-provider/model usage breakdown for the caller, ordered by total tokens. Source: https://docs.nasiko.com/api-reference/usage/per-providermodel-usage-breakdown-for-the-caller-ordered-by-total-tokens /api-reference/openapi.json get /api/usage/by-model # Agents accessible to a user (owner ∪ public ∪ direct user-grant; EE's Source: https://docs.nasiko.com/api-reference/users/agents-accessible-to-a-user-owner-∪-public-∪-direct-user-grant;-ees /api-reference/openapi.json get /api/users/{id}/accessible-agents Agents accessible to a user (owner ∪ public ∪ direct user-grant; EE's EE override additionally checks team/department grants). # Agents accessible to the caller — see `accessible_agents_for_user`. Source: https://docs.nasiko.com/api-reference/users/agents-accessible-to-the-caller-—-see-`accessible_agents_for_user` /api-reference/openapi.json get /api/users/me/accessible-agents # Change a user's role and immediately revoke their live tokens. EE wraps Source: https://docs.nasiko.com/api-reference/users/change-a-users-role-and-immediately-revoke-their-live-tokens-ee-wraps /api-reference/openapi.json put /api/users/{id}/role Change a user's role and immediately revoke their live tokens. EE wraps this with a leadership-displacement cascade — see the EE `ee_change_role` override. # Create a user. No password is taken — a one-time `access_key`/ Source: https://docs.nasiko.com/api-reference/users/create-a-user-no-password-is-taken-—-a-one-time-`access_key` /api-reference/openapi.json post /api/users Create a user. No password is taken — a one-time `access_key`/ `access_secret` pair is minted and returned once; the secret doubles as the user's login password (see `nasiko-user-creation-contract`). # Deactivate a user and revoke their live tokens. Rejects self-deactivation Source: https://docs.nasiko.com/api-reference/users/deactivate-a-user-and-revoke-their-live-tokens-rejects-self-deactivation /api-reference/openapi.json post /api/users/{id}/deactivate Deactivate a user and revoke their live tokens. Rejects self-deactivation and deactivating the last admin. # Delete a user. Rejects self-deletion, deleting the last admin, or a user Source: https://docs.nasiko.com/api-reference/users/delete-a-user-rejects-self-deletion-deleting-the-last-admin-or-a-user /api-reference/openapi.json delete /api/users/{id} Delete a user. Rejects self-deletion, deleting the last admin, or a user who still owns agents (reassign or delete those first). # Get a user by id (superuser-only). Source: https://docs.nasiko.com/api-reference/users/get-a-user-by-id-superuser-only /api-reference/openapi.json get /api/users/{id} # List every admin (`role = 'admin'`) user. Source: https://docs.nasiko.com/api-reference/users/list-every-admin-`role-=-admin`-user /api-reference/openapi.json get /api/users/admins # List users (superuser-only; EE additionally exposes a role/org-scoped Source: https://docs.nasiko.com/api-reference/users/list-users-superuser-only;-ee-additionally-exposes-a-roleorg-scoped /api-reference/openapi.json get /api/users List users (superuser-only; EE additionally exposes a role/org-scoped listing at `/api/org/users` — see the EE org-users routes). # Mint a fresh `access_key`/`access_secret` pair for a user, replacing any Source: https://docs.nasiko.com/api-reference/users/mint-a-fresh-`access_key``access_secret`-pair-for-a-user-replacing-any /api-reference/openapi.json post /api/users/{id}/regenerate-credentials Mint a fresh `access_key`/`access_secret` pair for a user, replacing any existing credential, and revoke their live tokens. # Reactivate a previously deactivated user. Source: https://docs.nasiko.com/api-reference/users/reactivate-a-previously-deactivated-user /api-reference/openapi.json post /api/users/{id}/reinstate # The caller's own user record (superuser-only route; EE overrides this Source: https://docs.nasiko.com/api-reference/users/the-callers-own-user-record-superuser-only-route;-ee-overrides-this /api-reference/openapi.json get /api/users/me The caller's own user record (superuser-only route; EE overrides this with the same EE-aware shape as `get_user` — see the EE users module). # Update a user's own-editable fields (superuser-only; EE overrides this Source: https://docs.nasiko.com/api-reference/users/update-a-users-own-editable-fields-superuser-only;-ee-overrides-this /api-reference/openapi.json put /api/users/{id} Update a user's own-editable fields (superuser-only; EE overrides this route to additionally accept `department_id`/`team_id` — see the EE `ee_update_user` override). An `is_active: false` transition here runs the same self-deactivate/last-admin guards as the dedicated `/deactivate` route. # A2A agents and frameworks Source: https://docs.nasiko.com/build/a2a What your container must expose to run on Nasiko, and how the platform talks to it. Nasiko runs containers that speak [A2A](https://a2a-protocol.org), an open JSON-RPC protocol for agentic applications. Implement the contract below and your container runs on Nasiko regardless of language, framework, or model. A2A treats each agent as an opaque peer: you send it a message, it works on a task, it returns artifacts. The platform never inspects an agent's prompts, tools, or reasoning. ## The agent contract A container must do three things: 1. **Serve an agent card** — JSON describing name, skills, and capabilities, over unauthenticated GET at `/.well-known/agent-card.json` (the legacy `/.well-known/agent.json` also works). 2. **Implement A2A JSON-RPC** — at minimum `message/send`. Add `message/stream` for token-by-token SSE responses. 3. **Respond to health checks** — HTTP 200 on the same endpoint. Nasiko doesn't enforce internal implementation or response quality. Whether an agent's declared skills work is on you. ### Agent card ```json theme={null} { "name": "Paper Research Agent", "description": "Searches arXiv and Semantic Scholar for academic papers", "version": "1.0.0", "supportedInterfaces": [ { "url": "http://localhost:8000/jsonrpc", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" } ], "capabilities": { "streaming": true, "pushNotifications": false }, "defaultInputModes": ["text/plain"], "defaultOutputModes": ["text/plain"], "skills": [ { "id": "paper-search", "name": "Paper Search", "description": "Search academic databases for papers by topic, author, or keywords", "tags": ["research", "papers", "arxiv", "academic"] } ] } ``` | Field | Required | Description | | - | - | - | | `name`, `description`, `version` | Yes | Identity — `version` should track your image tag | | `supportedInterfaces` | Yes | Transport endpoints (URL + protocol binding) | | `capabilities` | Yes | `streaming`, `pushNotifications` | | `defaultInputModes` / `defaultOutputModes` | Yes | Accepted and produced MIME types | | `skills` | Yes | What other agents and the routing engine reason about | | `provider`, `securitySchemes`, `documentationUrl`, `iconUrl` | No | Optional metadata | The platform reads the card once at deploy time. Changing what an agent can do means changing its source and redeploying, not editing metadata. ### Wire format A2A is JSON-RPC 2.0 over HTTP. A minimal `message/send`: ```json theme={null} { "jsonrpc": "2.0", "id": "req-001", "method": "message/send", "params": { "message": { "messageId": "msg-abc123", "role": "user", "parts": [{ "text": "Find papers about transformer architectures" }] } } } ``` The non-streaming response wraps the result in a `Task`: ```json theme={null} { "jsonrpc": "2.0", "id": "req-001", "result": { "task": { "id": "task-xyz789", "contextId": "ctx-def456", "status": { "state": "completed" }, "artifacts": [ { "artifactId": "art-001", "parts": [{ "text": "Here are the top papers..." }] } ] } } } ``` `message/stream` returns the same envelope as SSE: a `working` status, artifact chunks, then a terminal `completed` status. `contextId` ties a multi-turn conversation together. It's also what a Nasiko *session* groups in [observability](/governance/flow-limits). Official A2A SDKs exist for several languages, and the [sample agents](/build/registry/sample-agents) are working examples you can copy from. ## How Nasiko dispatches to agents | Request shape | What happens | | - | - | | `agent_id` names an agent | **Direct dispatch** — proxied to that agent's container | | No `agent_id`, or `agent_id: "orchestrator"` | **Routed dispatch** — the platform picks an agent | ```json theme={null} // Direct { "jsonrpc": "2.0", "method": "message/send", "params": { "message": { "role": "user", "parts": [{ "text": "Review this PR" }] }, "metadata": { "agent_id": "code-reviewer" } } } ``` ```json theme={null} // Routed { "jsonrpc": "2.0", "method": "message/send", "params": { "message": { "role": "user", "parts": [{ "text": "Review this PR" }] } } } ``` Routed dispatch runs a multi-turn ReAct loop: the orchestrator exposes every agent you can access as a callable tool, then an LLM decides across up to ten turns which agents to call and in what order. This is not the [routing engine](/governance/flow-limits)'s three-stage shortlist/rerank/select pipeline — no shortlisting or reranking happens here, and the orchestrator can call several agents per query. The routing engine is used elsewhere, to auto-assign agents to [MAF workflow](/build/workflows) steps. Either way, every hop is an ordinary A2A call proxied through the platform. An agent can't tell whether it was called directly or by the orchestrator. Agent identifiers accept a name or a UUID almost everywhere — except the agent proxy, which requires a UUID. If you're calling `/api/agents/{id}` directly, resolve the name first. ## Frameworks and languages | Language | Frameworks | | - | - | | Python | OpenAI Agents SDK, Anthropic SDK, CrewAI, LangChain/LangGraph, Google ADK, or a plain LLM SDK call | | Rust | Direct A2A server implementations | | Go | Direct A2A server implementations | If none fit, implement the three-item contract in any language that serves HTTP and SSE. There's no SDK requirement. See [scaffolding](/build/scaffold) for the template library. ## Version compatibility A2A response shapes differ subtly across versions — older responses put artifacts directly under `result`, newer ones under `result.task`. Nasiko's client tries current method names and dialects first, then falls back through older forms rather than hard-failing. Implement against current `message/send`/`message/stream` and the task-wrapped shape above. You don't need to special-case older dialects. ## Next Deploy and manage from the web app. The ReAct loop and the MAF assignment pipeline. Reference agents to copy from. Generate a new project from a template. # Deploying and managing agents Source: https://docs.nasiko.com/build/deploy Get your agent onto a cluster, then operate it — logs, scaling, restarts, secrets, teardown. Deploying is one command against a connected cluster. Day-2 operations use the same command set regardless of the runtime underneath. ## Connect to a cluster ```sh theme={null} nasiko connect https://nasiko.example.com --name prod nasiko auth login ``` Or run one locally: ```sh theme={null} nasiko up # Postgres, Redis, and the Nasiko server, registered as "local" nasiko clusters # list configured clusters nasiko use prod # switch the active cluster nasiko down # stop the local cluster ``` Every command below runs against the active cluster. ## Getting an image onto the cluster ```sh theme={null} nasiko deploy . # build, push, deploy nasiko deploy . --name my-agent nasiko deploy . --port 8080 nasiko deploy . --env-file .env nasiko deploy . -e API_KEY=sk-xxx ``` Builds the image locally, pushes it to the cluster's embedded registry, and starts the container. Names are unique per cluster — deploying the same name updates the existing agent. For CI, `nasiko push .` builds and pushes without starting the container. ```sh theme={null} nasiko upload . ``` No local Docker needed. Zips your source (or takes a `.zip`) and posts it; a background build worker builds and deploys it. Returns a build ID immediately (`202 Accepted`) — watch progress with `nasiko logs` or the Builds view. Every deployment is automatically instrumented for observability and receives its decrypted secrets as environment variables. No extra step for either. ## Day-2 operations | Command | What it does | | - | - | | `nasiko ps` | List running agents (`--json` for machine-readable output) | | `nasiko logs ` | Tail container logs (`-n 50` default; `-f` live-tails) | | `nasiko stop ` | Scale to zero, keeping config and state | | `nasiko start ` | Resume a stopped agent | | `nasiko restart ` | Recreate the container, picking up secret and env changes | | `nasiko scale ` | Scale to `n` replicas | | `nasiko rm --name ` | Terminate and deregister (`-f` skips confirmation; accepts a raw UUID positionally instead of `--name`) | | `nasiko status` | Cluster health and metrics | | `nasiko import ` | Pull an image the cluster is allowed to reach | | `nasiko deployments ls` | Deployment-level status | `restart` and `stop`/`start` are not interchangeable. `restart` tears down and recreates the container — this is what picks up changed secrets or environment variables. `stop`/`start` pause and resume without touching configuration. ## Secrets Secrets are encrypted environment variables injected at deploy and restart time, at two scopes: ```sh theme={null} # Vault — every agent you own nasiko secrets set OPENAI_API_KEY sk-xxx nasiko secrets ls nasiko secrets rm OPENAI_API_KEY # Agent-specific — overrides the vault nasiko secrets set DB_URL postgres://... --agent my-agent nasiko secrets ls --agent my-agent ``` Precedence, highest first: `nasiko deploy -e` → agent-specific secret → vault secret. A changed secret takes effect on `nasiko restart`. Full model: [secrets overview](/governance/secrets/overview). Day-2 detail including `--writable` and `import`: [Operate agents](/build/operate). ## Observability ```sh theme={null} nasiko observe # sessions, traces, spans, project stats, TokenOps, insights ``` Traces, logs, and cost are available as soon as your agent handles traffic. See [flow limits](/governance/flow-limits) for how a session groups a multi-agent conversation. ## Next History, reupload, rollback. Logs, scale, writable storage, teardown. # Connect your agent to the MCP gateway Source: https://docs.nasiko.com/build/mcp/agent-integration Wire any agent, in any language or framework, to discover and call every tool a user has connected — with no hardcoded tool names. Wire any agent — any language, any framework — to the Nasiko MCP gateway so it can discover and call every tool a user has connected, with zero hardcoded tool names. If your agent runs on Nasiko, it doesn't need an MCP SDK, a special client library, or any per-connector code. It needs exactly two things at deploy time, and one small tool-calling loop. This page covers that, in whatever framework you're already using — LangChain, LangGraph, CrewAI, Google ADK, the raw OpenAI SDK, Anthropic's SDK, or a hand-rolled agent in Node, Rust, or Go. For the router to work, each agent is given two environment variables at deploy time: `OPENAI_BASE_URL` (the gateway's address, not `api.openai.com`) and `OPENAI_API_KEY` — not a real provider key, but a signed ticket that says "I am agent X, acting on behalf of user Y." Your agent framework already reads both of these to construct its LLM client, so most of the wiring is already done for you — this page is about the **tool-calling** half, not the LLM half. ## How it works Every tool call your agent makes goes through one HTTP endpoint — `MCP_GATEWAY_URL` — using plain JSON-RPC 2.0. There is no MCP client SDK dependency: you `POST` a JSON body and read a JSON body back. The two methods you need are `tools/list` (what's available right now, for this user, for this agent) and `tools/call` (invoke one). Authentication is a single header, `x-nasiko-agent-token` — a short-lived, per-request credential minted by the platform and forwarded to your agent on every inbound call. You read it once, and forward the exact same value on every outbound call you make to the gateway during that request. This token is **not** your agent's static API key. It's minted fresh per inbound request and expires in minutes. Never cache it across requests, never log its full value, and never forward it anywhere except the gateway. ## Quickstart Injected automatically into your agent's environment at deploy time — already includes the full path. If it's empty, your agent either isn't deployed with the gateway enabled, or was deployed before it was — skip tool-calling gracefully rather than erroring out. ```python theme={null} MCP_GATEWAY_URL = os.environ.get("MCP_GATEWAY_URL", "") # already includes /api/mcp ``` Every request that reaches your agent carries `x-nasiko-agent-token` as a header. Pull it out using whatever your framework exposes for inbound headers. ```python a2a-sdk theme={null} def get_token(context: RequestContext) -> str | None: call_context = context.call_context if not call_context: return None return call_context.state.get("headers", {}).get("x-nasiko-agent-token") ``` ```python FastAPI / Starlette theme={null} from fastapi import Request async def handler(request: Request): token = request.headers.get("x-nasiko-agent-token") ``` ```python Flask theme={null} from flask import request token = request.headers.get("x-nasiko-agent-token") ``` ```javascript Express theme={null} app.post("/", (req, res) => { const token = req.headers["x-nasiko-agent-token"]; }); ``` ```go Go theme={null} token := r.Header.Get("X-Nasiko-Agent-Token") ``` If your framework doesn't expose inbound headers anywhere in the handler context, wrap it in middleware that copies them somewhere your handler can reach: ```python theme={null} class TokenMiddleware: def __init__(self, app): self.app = app async def __call__(self, scope, receive, send): if scope["type"] == "http": headers = dict(scope["headers"]) scope["state"]["mcp_token"] = headers.get(b"x-nasiko-agent-token", b"").decode() or None await self.app(scope, receive, send) ``` No token present? Skip tool access for this request — don't crash. One helper function covers both methods you'll ever need. ```python theme={null} async def mcp_call(method: str, params: dict | None, token: str) -> dict: body = {"jsonrpc": "2.0", "id": str(uuid.uuid4()), "method": method} if params is not None: body["params"] = params async with httpx.AsyncClient(timeout=60.0) as client: resp = await client.post(MCP_GATEWAY_URL, headers={"x-nasiko-agent-token": token}, json=body) return resp.json() ``` Never hardcode tool names — they're per-user and change whenever a connector is added or removed. Always call `tools/list` fresh. See [Protocol reference](#protocol-reference) for exact request and response shapes. Add two function-calling tools **alongside** whatever tools your agent already has — don't replace them. ```python theme={null} tool_defs = existing_tools + [ {"type": "function", "function": {"name": "mcp_list_tools", "description": "List available MCP tools.", "parameters": {"type": "object", "properties": {}}}}, {"type": "function", "function": {"name": "mcp_call_tool", "description": "Call an MCP tool by exact name from mcp_list_tools.", "parameters": {"type": "object", "properties": { "tool_name": {"type": "string"}, "arguments": {"type": "object"} }, "required": ["tool_name"]}}}, ] ``` Dispatch by name in whatever tool-execution code you already have — one branch, nothing else changes: ```python theme={null} if call.function.name == "mcp_list_tools": result = await mcp_call("tools/list", None, token) elif call.function.name == "mcp_call_tool": args = json.loads(call.function.arguments) result = await mcp_call("tools/call", {"name": args["tool_name"], "arguments": args.get("arguments", {})}, token) else: result = run_my_existing_tool(call) # unchanged ``` Name collision with a tool you already have? Rename either side — there's no fixed contract, `mcp_list_tools` and `mcp_call_tool` are just this page's convention. LLMs tend to refuse a tool instead of trying it when they're not 100% sure it's relevant. Push back in your system prompt: > "Always call mcp\_list\_tools first. If any tool plausibly relates to the request, call it with best-effort arguments — don't ask for clarification, don't skip it because you're unsure. Only say nothing's available after actually trying." If you're not using a framework with a built-in agent loop, cap the manual one and make sure a mid-loop cutoff doesn't silently drop results. ```python theme={null} for _ in range(10): # cap the loop resp = await llm.chat.completions.create(model=model, messages=messages, tools=tool_defs) choice = resp.choices[0].message messages.append(choice.model_dump(exclude_none=True)) if not choice.tool_calls: return choice.content for call in choice.tool_calls: result = dispatch(call) # your logic from the previous step messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)}) # hit cap mid-tool-use: don't drop results, force a final answer messages.append({"role": "user", "content": "Stop calling tools. Summarize what you found."}) return (await llm.chat.completions.create(model=model, messages=messages)).choices[0].message.content ``` ```python theme={null} try: result = await mcp_call(...) except httpx.TimeoutException: return "That tool call took too long and was cancelled." ``` Use 30–60s or longer timeouts for gateway calls, not a short default — real connectors (Notion, Slack, and the like) can be genuinely slow. A caught-but-unmessaged timeout often logs as an empty string and looks to the user like a silent crash. If you're using LangChain, LangGraph, CrewAI, or Google ADK, skip this step entirely — see [Framework integrations](#framework-integrations) below, your framework already runs this loop for you. ## Protocol reference Plain JSON-RPC 2.0 over a single endpoint (`MCP_GATEWAY_URL`), always `POST`, always HTTP 200 — a failed tool call is a JSON-RPC `error` object in the body, not an HTTP error status. ### Methods | Method | Params | Result | Notes | | - | - | - | - | | `initialize` | — | Handshake info | Optional. Only needed if wrapping the gateway in a real MCP client SDK — skip it if calling the gateway directly as shown above. | | `ping` | — | `{}` | Optional liveness check. | | `tools/list` | — | `{"tools": [{"name", "description", "inputSchema"}]}` | Per-user, per-agent. Changes any time the user connects or disconnects a service, or your agent's permissions change. Always fetch fresh — never cache across turns or sessions. | | `tools/call` | `{"name", "arguments"}` | `{"content": [...], "isError": bool}` or `{"error": {"code", "message"}}` | `name` must be the exact namespaced string from `tools/list` (`connectorId__toolName`) — treat it as opaque, never construct it yourself. | ### Error codes | Code | Meaning | What your agent should do | | - | - | - | | `-32000` | Blocked by the user's own permission rules | Tell the user it's blocked. Don't retry, don't silently substitute a different tool. | | `-32001` | Needs human approval (an "ask"-stance tool) | Tell the user approval is pending. Don't retry-loop. | | `-32602` | Missing or invalid delegation token | A bug in your own header forwarding — not user-facing. | | `-32601` | Unknown method | Check your `method` string. | | Any other | A real failure from the underlying service | Report it honestly. Never fake success, never silently swap in a fallback the user didn't ask for. | ## Framework integrations Pick your framework. In every case, the pattern is the same: wrap the two gateway calls as tools your framework already knows how to call, and add them to whatever tool list you already have. ### LangChain ```python theme={null} from langchain_core.tools import tool @tool async def mcp_list_tools() -> dict: """List available MCP tools.""" return await mcp_call("tools/list", None, token) @tool async def mcp_call_tool(tool_name: str, arguments: dict) -> dict: """Call an MCP tool by exact name from mcp_list_tools.""" return await mcp_call("tools/call", {"name": tool_name, "arguments": arguments}, token) agent_executor = AgentExecutor( agent=agent, tools=existing_tools + [mcp_list_tools, mcp_call_tool], max_iterations=10, # = the loop cap early_stopping_method="generate", # = force a final answer instead of dropping results ) ``` ### LangGraph ```python theme={null} from langgraph.prebuilt import create_react_agent graph = create_react_agent(model, tools=existing_tools + [mcp_list_tools, mcp_call_tool]) result = await graph.ainvoke({"messages": [...]}, config={"recursion_limit": 10}) # = the loop cap ``` `mcp_list_tools` and `mcp_call_tool` are the same `@tool`-decorated functions from the LangChain section above — LangGraph's prebuilt ReAct agent accepts the same tool objects. ### CrewAI Same pattern as LangChain: wrap the two functions as `@tool`-decorated CrewAI tools, add them to the agent's `tools` list, and set `max_iter` on the agent to bound the loop. ### Google ADK ADK derives a tool's schema from a plain Python function's type hints and docstring — no decorator needed, just pass the functions directly: ```python theme={null} from google.adk.agents import Agent async def mcp_list_tools() -> dict: """List available MCP tools.""" return await mcp_call("tools/list", None, token) async def mcp_call_tool(tool_name: str, arguments: dict) -> dict: """Call an MCP tool by exact name from mcp_list_tools. Args: tool_name: Exact namespaced tool name from mcp_list_tools. arguments: Arguments object for the tool call. """ return await mcp_call("tools/call", {"name": tool_name, "arguments": arguments}, token) root_agent = Agent( model="gemini-2.0-flash", tools=existing_tools + [mcp_list_tools, mcp_call_tool], ) ``` ### OpenAI Agents SDK and raw SDK See [Quickstart](#quickstart) above — the manual loop shown there *is* the raw-SDK pattern. If you're on the OpenAI Agents SDK specifically, wrap the two functions with `@function_tool` and add them to your `Agent`'s `tools` list the same way; the SDK's own `Runner` loop handles the rest. ### Anthropic Claude (native tool use) Claude's native tool-use format isn't OpenAI-compatible: the schema field is `input_schema` (not `parameters`), and tool results go back as a `tool_result` content block, not a `role: tool` message. ```python theme={null} tool_defs = existing_tools + [ {"name": "mcp_list_tools", "description": "List available MCP tools.", "input_schema": {"type": "object", "properties": {}}}, {"name": "mcp_call_tool", "description": "Call an MCP tool by exact name.", "input_schema": {"type": "object", "properties": { "tool_name": {"type": "string"}, "arguments": {"type": "object"} }, "required": ["tool_name"]}}, ] for block in response.content: if block.type == "tool_use": result = dispatch_by_name(block.name, block.input) # same dispatch logic as the Quickstart messages.append({"role": "user", "content": [ {"type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result)} ]}) ``` ### Node.js and TypeScript No SDK dependency needed — a raw `fetch` call works with any Node framework or agent library: ```typescript theme={null} async function mcpCall(method: string, params: object | null, token: string) { const body = { jsonrpc: "2.0", id: crypto.randomUUID(), method, ...(params ? { params } : {}) }; const resp = await fetch(process.env.MCP_GATEWAY_URL!, { method: "POST", headers: { "x-nasiko-agent-token": token, "Content-Type": "application/json" }, signal: AbortSignal.timeout(60_000), body: JSON.stringify(body), }); return resp.json(); } ``` ### Rust ```rust theme={null} async fn mcp_call(client: &reqwest::Client, gateway_url: &str, method: &str, params: Option, token: &str) -> Value { let mut body = json!({ "jsonrpc": "2.0", "id": Uuid::new_v4().to_string(), "method": method }); if let Some(p) = params { body["params"] = p; } client .post(gateway_url) .header("x-nasiko-agent-token", token) .timeout(std::time::Duration::from_secs(60)) .json(&body) .send() .await .and_then(|r| r.json::()) // handle the Result properly in real code .unwrap_or_else(|e| json!({ "error": { "message": e.to_string() } })) } ``` ## Local testing Test with `curl` before wiring up any agent code — this isolates "is my token and URL correct" from "is my agent code correct." ```bash theme={null} curl -s -X POST "$MCP_GATEWAY_URL" \ -H "x-nasiko-agent-token: $TOKEN" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":"1","method":"tools/list"}' | jq ``` A non-empty `result.tools` array means your token and URL are both correct — any remaining problem is in your agent code, not the gateway. A `-32602` means your token is invalid, expired, or missing; grab a fresh one from a real inbound request rather than reusing an old value. ## Security * Never log the full token value — log `token present: true/false` only. * Never forward the token to a third-party service, or echo it back to the end user in a response. * Never cache or persist the token — it's minted fresh per inbound request and expires in minutes by design. * The token is the *only* credential this endpoint accepts. There's no separate API key to rotate or manage. ## Troubleshooting | Symptom | Likely cause | | - | - | | `tools/list` returns an empty array | Not a bug — the user has no connectors configured yet, or none granted to this specific agent. Check the connector settings. | | Every gateway call returns `-32602` | Token isn't being forwarded correctly, or you're reading the wrong header. Double-check you're reading `x-nasiko-agent-token` exactly. | | Agent works, then randomly "loses" tool access mid-session | You're caching the token across requests — re-read it from the inbound request every time. | | Agent ignores an obviously relevant tool | System prompt isn't pushing the model hard enough to try tools — see the prompt guidance in Quickstart step 4. | | Blank or empty error message on a slow request | Timeout too short, or the exception isn't caught explicitly — see Quickstart step 5. | | Agent answers using stale or wrong tool names | Tool list is being cached — always call `tools/list` fresh, every turn. | | `MCP_GATEWAY_URL` is empty in production | Agent was deployed before the gateway was enabled for it — redeploy, a restart alone won't pick up newly injected environment variables. | ## Checklist Before calling your integration done, verify each of these against a real deployment: * `MCP_GATEWAY_URL` missing — agent still answers plain questions, no crash * Token missing — same, graceful skip * `tools/list` returns tools when the user has a connector configured * `tools/call` result reaches the LLM and is used in the final answer * No connector for the topic — an honest "nothing available," never a made-up answer * A bad tool call reports the real error, with no silent fallback to something else * A slow tool produces a clean timeout message, not a blank crash * Pre-existing tools still work unchanged, and the LLM can call one of yours and one gateway tool in the same turn ## Related * [Connect an external MCP server](/build/mcp/external-server) — register a managed toolkit or your own already-running MCP server as a connector your agents can use * [Per-agent tool permissions](/governance/tool-permissions) — control which connectors and tools an agent may call * [A2A agents and frameworks](/build/a2a) — how agents themselves are invoked and discovered on the platform * [Agent development lifecycle](/build/overview) — end-to-end: scaffold, build, deploy, and version an agent # Deploy your own MCP server Source: https://docs.nasiko.com/build/mcp/deploy-server Upload your MCP server's source and have Nasiko build, harden, and deploy it as a connector. Upload a `.zip` or point at a GitHub repo, and the platform builds a container image, deploys it hardened, waits for a healthy handshake, and registers it as a connector. From then on it behaves like any other connector — shareable, permissioned per agent, callable through the [gateway](/build/mcp/overview). Already have a running server? See [Connect an external MCP server](/build/mcp/external-server). ## What your server must provide * **A `Dockerfile` at the source root.** The only hard requirement — a missing `Dockerfile`, or one with no `FROM`, is rejected before any build. * **Read `$PORT` and bind `0.0.0.0:$PORT`.** The platform assigns the port (8080 by default). * **Serve MCP over Streamable HTTP at `/mcp`.** Language and SDK detection is a diagnostic hint only, never enforced. An unrecognized layout still gets a real build attempt — the correctness gate is the live handshake after deploy. Uploaded zips are capped at 50 MB by default. For private-repo access, use the GitHub path — it clones over HTTPS from allow-listed hosts and can use your connected GitHub account. ## Upload it ```bash CLI — local zip theme={null} nasiko mcp connector upload \ --name my-mcp-server \ --zip ./my-mcp-server.zip \ --version v1 \ --env "STRIPE_KEY=sk_live_..." ``` ```bash CLI — GitHub repo theme={null} nasiko mcp connector upload-github \ --name my-mcp-server \ --github-url https://github.com/yourorg/my-mcp-server \ --version v1 \ --env "STRIPE_KEY=sk_live_..." ``` ```json REST — zip (multipart/form-data) theme={null} POST /api/mcp/connectors/upload name=my-mcp-server version_tag=v1 env={"STRIPE_KEY":"sk_live_..."} source=@my-mcp-server.zip ``` `--env` (repeatable) sets secrets your server reads at runtime — encrypted at rest, injected at deploy time. This is distinct from a connector *credential* (`nasiko mcp credential set`), which authenticates the gateway to your server, not your server to the API it wraps. Both commands return immediately; the build runs asynchronously: ```json theme={null} { "data": { "connector_id": "a1b2c3d4-...", "build_id": "e5f6a7b8-..." }, "status_code": 202, "message": "MCP server build queued" } ``` Uploading requires the same permission as deploying an agent — any authenticated user by default. See [access control](/governance/access-control/overview). ## Watch it build ```bash theme={null} nasiko mcp connector build-status nasiko mcp connector logs --tail 200 ``` ``` GET /api/mcp/connectors/{id}/build-status GET /api/mcp/connectors/{id}/build-logs?tail=200 ``` `build-status` reports `pending`, `building`, `running`, or `failed`. Poll it — there's no push notification. `logs` returns the container's stdout and stderr. ## What happens during the build 1. **Source acquired** — the zip is extracted, or the repo is cloned. 2. **Validation** — the `Dockerfile` requirement is checked. Language detection is a hint, never a rejection. 3. **Image build** — built from your `Dockerfile`. 4. **Hardened deploy** — a fixed non-root user, read-only root filesystem, all Linux capabilities dropped, no privilege escalation. Placed on a network segment dedicated to uploaded MCP servers, separate from agent traffic and the platform's database and cache. Its network policy allows receiving tool calls but not initiating outbound agent-to-agent traffic. 5. **Readiness check** — the platform calls `tools/list` against your server, retrying for a few minutes to absorb a cold start. The connector flips to `running` — and becomes visible — only once this succeeds. 6. **Tool catalog sync** — every reported tool is recorded so permission screens show them instantly. Missing descriptions are generated automatically. If a **first-time** upload fails at build or readiness, the connector record is removed so you can re-upload under the same name. If the connector had deployed successfully before, a failed re-upload marks it `failed` while keeping its history and grants intact. ## Self-heal A restart, redeploy, or host reboot can change a container's address. Because the platform resolved that address itself — unlike a user-typed external URL — the gateway is allowed to refresh it: if a tool call fails to connect, it looks up the current address and retries once. On demand after a real failure, never as a background poll. ## Once it's running * [Connect an external MCP server](/build/mcp/external-server) — sharing commands apply the same way * [Per-agent tool permissions](/governance/tool-permissions) — enable per agent, set per-tool rules * [Artifact registry](/build/registry/overview) — publish your source as a reusable artifact ## Related * [MCP overview](/build/mcp/overview) — the gateway and delegation token * [Agent hosting](/self-hosting/agent-hosting) — how builds are queued, retried, recovered # Connect an external MCP server Source: https://docs.nasiko.com/build/mcp/external-server Register an MCP server by URL, connect credentials, share it, and handle OAuth 2.1. If a tool already exists — a managed SaaS integration, or an MCP server you or a third party runs — connect it rather than build one. This page covers registering a server by URL. Have source code instead? See [Deploy your own server](/build/mcp/deploy-server). Everyone connects with **their own credential or OAuth grant** — sharing never hands over the owner's login. A custom MCP server is invisible to everyone but its owner until shared. ## Probe before registering ```bash CLI theme={null} nasiko mcp connector probe https://mcp.example.com/mcp ``` ```bash REST theme={null} curl -X POST https://your-control-plane/api/mcp/connectors/probe \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"url": "https://mcp.example.com/mcp"}' ``` The probe checks for OAuth discovery metadata, then falls back to a live `initialize` handshake. ```json theme={null} { "data": { "url": "https://mcp.example.com/mcp", "auth_type": "oauth2", "requires": "oauth_flow", "supports_dcr": true, "hint": "This server supports OAuth 2.1 with automatic client registration — no client credentials needed." }, "status_code": 200, "message": "Connector probed successfully" } ``` `supports_dcr: false` means you'll need to supply your own OAuth client ID and secret. A server wanting a bearer token reports `"auth_type": "bearer"`. ## Register the connector ```bash CLI — bearer token theme={null} nasiko mcp connector register my-internal-api https://mcp.example.com/mcp \ --auth-type bearer \ --description "Internal ticketing API" ``` ```bash CLI — OAuth 2.1 theme={null} nasiko mcp connector register my-internal-api https://mcp.example.com/mcp \ --auth-type oauth2 \ --oauth-client-id "$CLIENT_ID" \ --oauth-client-secret "$CLIENT_SECRET" ``` ```json REST theme={null} POST /api/mcp/connectors { "name": "my-internal-api", "url": "https://mcp.example.com/mcp", "transport": "streamable_http", "auth_type": "bearer", "description": "Internal ticketing API" } ``` | `auth_type` | Injected on every call | Notes | | - | - | - | | `none` | Nothing | — | | `bearer` | `Authorization: Bearer ` | Custom header via `--credential-header-name`; auto-prefixed with `Bearer` | | `basic` | `Authorization: Basic ` | Encoded on connect | | `url_param` | `?=` on the request URL | Name it with `--url-param-name` | | `oauth2` | `Authorization: Bearer ` | Refreshed automatically as it nears expiry | Repeat `--header "Key: Value"` for extra static headers the server requires. Registration validates the URL against private, loopback, link-local, and cloud-metadata ranges, and the outbound client re-resolves and re-checks at connect time. A hostname that resolves publicly at registration and privately later is still rejected. Custom MCP servers must be publicly reachable. ## Connect your credential Registering connects nobody. Each person connects with their own credential: ```bash CLI theme={null} nasiko mcp connect --connector-id --value "$MY_API_TOKEN" ``` ```bash REST theme={null} curl -X POST https://your-control-plane/api/mcp/connect \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"connector_id": "", "credentials": {"value": "'"$MY_API_TOKEN"'"}}' ``` For `none`, `bearer`, `basic`, and `url_param` this connects immediately. For `oauth2` it starts the OAuth flow below. ```bash theme={null} nasiko mcp credential set "$MY_API_TOKEN" nasiko mcp credential status nasiko mcp credential delete ``` Credential endpoints are write-only — `credential status` reports only whether a value is stored, and no response ever echoes a credential. ## OAuth 2.1 ```bash theme={null} nasiko mcp oauth authorize # opens or prints an authorization URL; after consent, the platform's # callback exchanges the code and stores your token nasiko mcp oauth status nasiko mcp oauth revoke ``` ```json REST — begin the flow theme={null} POST /api/mcp/connectors/{id}/oauth/authorize ``` The redirect target, `GET /api/mcp/oauth/callback`, is public and state-verified rather than JWT-gated — a browser redirect can't carry your session. It validates the signed PKCE state before exchanging the code and storing encrypted tokens. ## Share it A connector is private until shared. Share with a user, publicly, or with a specific agent: ```bash With a user theme={null} nasiko mcp connector share add --user alice ``` ```bash Publicly theme={null} nasiko mcp connector share add --public ``` ```bash With one agent theme={null} nasiko mcp connector grant-agent ``` ```bash theme={null} nasiko mcp connector share list nasiko mcp connector share remove --user alice nasiko mcp connector revoke-agent nasiko mcp connector consumers ``` Once shared, a connector is usable on **every agent that person runs** immediately. Team and department sharing is an org-level operation — see [per-agent tool permissions](/governance/tool-permissions). Revoking a share also removes the grantee's stored credential, so a re-grant starts from a clean reconnect. ## Related * [MCP overview](/build/mcp/overview) — the gateway, delegation token, and request flow * [Deploy your own server](/build/mcp/deploy-server) — build from source instead * [Per-agent tool permissions](/governance/tool-permissions) — restricting tools, team sharing * [Access control overview](/governance/access-control/overview) # MCP gateway overview Source: https://docs.nasiko.com/build/mcp/overview How agents get tool access: connectors, per-agent permissions, and the gateway endpoint. Every deployed agent gets **one fixed URL** for every tool action, no matter how many tools or providers sit behind it: the **MCP gateway**. Adding, removing, sharing, or restricting a tool is a configuration change — the agent is never redeployed. The gateway speaks the [Model Context Protocol](https://modelcontextprotocol.io). Agents call it with JSON-RPC `tools/list` and `tools/call`, and it fans each call out to whichever backend implements that tool. Register a managed integration or your own running MCP server. Upload source and let Nasiko build, harden, and run it. Control which connectors and tools an agent may use. Register connectors and set rules in the web app. ## Two kinds of provider, one interface | Term | Meaning | | - | - | | **Toolkit** | A managed, pre-built integration (Gmail, Slack, GitHub, Google Calendar) — OAuth and credential refresh handled for you | | **MCP server** | A server speaking MCP directly — one you run elsewhere and register by URL, or one you upload for Nasiko to build and deploy | | **Connector** | The neutral term for a registry row representing either kind | Everyone using a shared connector connects with **their own account**. Sharing never shares the underlying login. ## Agent credential An agent is untrusted code serving many users. At deploy time Nasiko injects `MCP_GATEWAY_TOKEN` (and `MCP_GATEWAY_URL`) into the container. The agent sends that token on every gateway call. User identity still comes from the flow's `traceparent` — the same strict attribution as model calls. See [cost attribution](/tokenops/attribution). Two equivalent endpoints: | Method | Path | Auth | | - | - | - | | `POST` | `/api/mcp` | Token in `MCP_GATEWAY_TOKEN` / header | | `POST` | `/api/mcp/s/{token}` | Token in the URL | JSON-RPC methods: `initialize`, `ping`, `tools/list`, `tools/call`. A user session JWT is not accepted on `/api/mcp`. Management routes (`/api/mcp/catalog`, connectors, grants) use your login. The gateway JSON-RPC surface uses the deploy-time agent token. ## The gateway endpoint JSON-RPC `tools/list` and `tools/call` examples: ```json tools/list request theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tools/list" } ``` ```json tools/list response theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "GMAIL_SEND_EMAIL", "description": "Send an email via Gmail", "inputSchema": { "...": "..." } }, { "name": "a1b2c3d4__search_docs", "description": "Search internal documentation", "inputSchema": { "...": "..." } } ] } } ``` ```json tools/call request theme={null} { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "GMAIL_SEND_EMAIL", "arguments": { "to": "user@example.com", "subject": "Hi", "body": "..." } } } ``` ```json tools/call response theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "status": "sent" } } ``` Tool names from custom MCP server connectors are namespaced `{connector-id-prefix}__{tool_name}`, so two connectors can't collide. Managed toolkit tools keep their natural names (`GMAIL_SEND_EMAIL`, `SLACK_POST_MESSAGE`). ### Blocked and approval-required calls A `tools/call` can return a JSON-RPC error instead of a result: ```json blocked theme={null} { "jsonrpc": "2.0", "id": 2, "error": { "code": -32000, "message": "Tool 'GMAIL_SEND_EMAIL' is blocked or disabled for this agent." } } ``` ```json approval required theme={null} { "jsonrpc": "2.0", "id": 2, "error": { "code": -32001, "message": "Tool 'GMAIL_SEND_EMAIL' requires user approval. Grant access in the agent settings.", "data": { "server": "gmail" } } } ``` ## Permissions Every tool call resolves through two layers, in order: 1. **Reachability** — can the calling user reach this connector? (They own it, it's shared with them, or it's a globally available toolkit.) 2. **Per-agent permission** — is the connector enabled for this agent, and is this tool allowed, blocked, or gated behind approval? Nothing needs configuring to grant an agent access to a connector its caller can already reach — access propagates the moment a connector is shared. Full model: [per-agent tool permissions](/governance/tool-permissions). ## Management routes Session-authenticated and access-controlled — everything the CLI and dashboard use: | Method | Endpoint | Purpose | | - | - | - | | `GET` | `/api/mcp/catalog` | Browse connectable services | | `POST` | `/api/mcp/connect` | Unified connect, any auth type | | `GET` / `POST` | `/api/mcp/connectors` | List visible connectors / register a custom server | | `POST` | `/api/mcp/connectors/probe` | Detect a server's auth type before registering | | `GET` | `/api/mcp/connectors/my-uploads` | Your uploaded servers and build state | | `GET` | `/api/mcp/connectors/{id}/grants` | List a connector's shares | | `POST`/`DELETE` | `/api/mcp/connectors/{id}/grants/public` | Grant or revoke public access | | `POST`/`DELETE` | `/api/mcp/connectors/{id}/grants/users/{user_id}` | Grant or revoke access for one user | | `POST`/`DELETE` | `/api/mcp/connectors/{id}/grants/agents/{agent_id}` | Grant or revoke access for one agent | | `POST` | `/api/mcp/connectors/upload` | Upload a zip — build and deploy | | `POST` | `/api/mcp/connectors/upload-github` | Same, from a GitHub URL | | `GET` | `/api/mcp/connectors/{id}/build-status`, `/build-logs` | Poll build progress | | `GET`/`PUT` | `/api/mcp/agents/{agent_id}/tools` | View / bulk-update per-tool rules | | `DELETE` | `/api/mcp/agents/{agent_id}/permissions` | Reset to full default-allow | ## Related * [Artifact registry](/build/registry/overview) — MCP server packages as reusable artifacts * [Access control overview](/governance/access-control/overview) # Operate agents Source: https://docs.nasiko.com/build/operate Logs, scale, restarts, deployments, writable storage, and teardown after an agent is deployed. Day-2 operations use the same commands whether the agent was deployed with `deploy`, `upload`, or `import`. Every command runs against the [active cluster](/install). ## Status and logs ```bash theme={null} nasiko ps # running agents; --json for machines nasiko logs my-agent # last 50 lines nasiko logs my-agent -n 200 -f nasiko status # cluster health ``` `-f` live-tails via SSE. ## Start, stop, restart, scale ```bash theme={null} nasiko stop my-agent nasiko start my-agent nasiko restart my-agent nasiko scale my-agent 3 ``` `restart` tears down and recreates the container — this is what picks up changed secrets or environment variables. `stop` / `start` pause and resume without touching configuration. Under Docker, any scale above `1` is clamped to `1`. Horizontal scaling needs the Kubernetes runtime (Enterprise). See [Agent hosting](/self-hosting/agent-hosting). ## Deployments A deployment is one running instance of an agent, with crash reason and restart count. ```bash theme={null} nasiko deployments ls nasiko deployments get my-agent nasiko deployments restart ``` `deployments restart` restarts by deployment ID (from `ls`), not by agent name. Container-level `nasiko restart ` is the usual choice. ## Writable storage By default an agent's filesystem is ephemeral. To keep a directory across restarts, redeploys, and code updates: ```bash theme={null} nasiko deploy . --writable nasiko deploy . --writable-path /data nasiko upload . --writable ``` `--writable` mounts a private-per-agent volume at `/workspace`. `--writable-path` mounts at that absolute path instead (and implies `--writable`). Pick a dedicated state directory — the mount hides whatever the image ships at that path. ## Import from a registry ```bash theme={null} nasiko import owner/name:tag nasiko import registry.example.com/owner/name:tag ``` The cluster pulls the image itself. No local Docker needed. The registry host must be listed in `REGISTRY_IMPORT_ALLOWED_HOSTS` on the server. ## Remove ```bash theme={null} nasiko rm --name my-agent nasiko rm -f ``` Terminates the container and deregisters the catalog entry. `-f` skips confirmation. ## Secrets and env ```bash theme={null} nasiko secrets set OPENAI_API_KEY sk-xxx --agent my-agent nasiko restart my-agent ``` Precedence, highest first: `nasiko deploy -e` → agent-specific secret → vault secret. Full model: [agent secrets](/governance/secrets/agent-secrets). ## Next Version history, reupload, rollback. Every flag. # Build and deploy agents Source: https://docs.nasiko.com/build/overview Run agents built on any framework: scaffold, run locally, deploy, version, and operate. Nasiko runs agents built on any framework or language. If your container speaks [A2A](/build/a2a), Nasiko can run it, route to it, trace it, and govern the tools it calls. You don't need an SDK in your code path. Every agent follows the same agent development lifecycle, whether it uses the OpenAI Agents SDK, LangGraph, CrewAI, Google ADK, the Anthropic SDK, or a hand-written HTTP server in Rust or Go. You can scaffold and run an agent locally without a cluster. ```mermaid theme={null} flowchart LR S["Scaffold
nasiko new"] --> L["Run locally
nasiko run / chat"] L --> D["Deploy
nasiko deploy / upload / import"] D --> V["Version
nasiko versions / rollback"] V --> O["Operate
nasiko ps / logs / scale"] O -- "code change" --> D ``` `nasiko new` generates an `AgentCard.json`, a `Dockerfile`, and starter source from a template. `nasiko card` keeps the card in sync with your code, and `nasiko validate` checks the project structure. `nasiko run` builds the image and starts the container on your machine. It speaks A2A as soon as it starts, so you can talk to it with `nasiko chat` before anyone else sees it. Build locally and push with `nasiko deploy`, let the Nasiko server build from source with `nasiko upload`, or deploy a published image with `nasiko import`. Every deployment is traced and receives its secrets, an LLM router credential, and an MCP gateway credential. Each deploy records an immutable version. `nasiko versions` shows the history and `nasiko rollback` returns to an earlier version without a rebuild. List, inspect, scale, restart, and remove agents from the CLI or the **Your agents** and **Builds** screens in the dashboard. New to Nasiko? [Quickstart: deploy an agent](/quickstart-deploy) walks through the whole lifecycle in a few minutes. Generate a project and its agent card. Build, run, and chat before touching a cluster. Deploy, push, upload, import, or clone from GitHub. Immutable versions, history, and rollback. Logs, scaling, restarts, persistent storage, teardown. The contract your container implements. ## Beyond a single agent * [Routing engine](/build/routing-engine) explains how chat without a named agent reaches the right one. * [Workflows](/build/workflows) chain several agents into a repeatable multi-agent workflow. * [MCP gateway](/build/mcp/overview) gives every deployed agent one endpoint for every tool a user has connected. * [Registry](/build/registry/overview) covers the built-in image registry and the sample agents. ## Before you start You need Docker (or Podman) and the `nasiko` CLI. See [Install the CLI and connect to a cluster](/install). # Artifact registry Source: https://docs.nasiko.com/build/registry/overview Push, pull, and browse agents, skills, tools, and other artifacts. **Enterprise feature.** The artifact registry is a standalone `nasiko-ee` service — no route, module, or crate under the open-source edition hosts it. The OSS `nasiko` CLI ships only a generic client (`nasiko registry connect/search/list`) that can point at a registry URL you supply; it doesn't bundle or default to one. The artifact registry is an OCI-compliant store for everything you build — agents, skills, tools, MCP servers, standalone binaries. It speaks the same protocol as `docker`, `podman`, and `oras`, with a metadata and search layer on top. Reach it three ways: its own web UI, the `nasiko` and `nasiko-ee` CLIs, or the `/v1` (metadata) and `/v2` (OCI) HTTP APIs. ## Artifact types | Type | What it is | | - | - | | `agent` | A runnable A2A agent | | `skill` | A reusable skill bundle | | `tool` | A tool or function artifact | | `mcp` | An MCP server package | | `executable` | A standalone binary or CLI — how the platform CLI itself is distributed | Packaging is separate from type. An artifact can ship as plain source, a Dockerfile or Compose recipe, a multi-layer OCI image, or an external reference card with no blob at all. ## Publishing **The registry publish command** auto-detects agents (`AgentCard.json` at the root) and skills (`skill.json`). For anything else, pass `--type` with an `artifact.json` manifest: ```sh theme={null} nasiko-ee registry publish ./my-agent --owner myorg ``` If the path isn't a local directory, it's treated as an image reference and pushed as OCI layers. **A standard OCI push** — `docker push`, `podman push`, or `oras push` — works against `/v2` with any compliant client. The registry still classifies the artifact for its catalog, best-effort and never rejecting the push: 1. An explicit `org.nasiko.type` annotation: ```sh theme={null} oras push registry.example.com/myorg/my-cli:v1 ./nasiko-ee \ --annotation "org.nasiko.type=executable" ``` 2. Otherwise, inference from OCI media types — an OCI 1.1 `artifactType`, a Helm chart's config media type. 3. Otherwise `unknown`, grouped under "Miscellaneous." A mis-typed artifact isn't stuck — an admin can correct its type, description, tags, license, or framework without re-publishing. ## Pulling * **Image-packaged** — `docker pull` / `podman pull` / `oras pull` against `registry.example.com/{owner}/{repo}:{tag}` * **Single-blob** (source, open-standard bundles) — `/v1/artifacts/{owner}/{name}/{version}/download` * **From the CLI:** ```sh theme={null} nasiko registry connect https://registry.example.com nasiko registry search "nutrition planning" --type agent nasiko registry list --type skill ``` `nasiko registry` also supports `disconnect`, `status`, and `--json`. `search` (alias `discover`) takes `--framework`, `--top`, and `--min-score`. ## Browsing and searching The web UI shows a card grid with type tabs — **All**, **Agents**, **Skills**, **MCP**, and **Miscellaneous** (which appears only when there's an uncategorized artifact). Framework and format facet filters hide themselves when they wouldn't narrow anything. Search takes a natural-language query and ranks semantically when an embeddings provider is configured. Without one, it falls back to keyword search transparently — same API, same UI, no error. ## Lifecycle and admin edits Each version carries a status: `preview`, `stable`, or `verified`. An admin sets these in the UI's edit-details view or via the metadata API: ```sh theme={null} curl -X PATCH https://registry.example.com/v1/artifacts/myorg/my-agent/v1 \ -u admin:secret \ -H "Content-Type: application/json" \ -d '{"status": "verified", "tags": ["nlp", "customer-support"]}' ``` `yanked` isn't settable this way — removing a version is a deliberate separate action: `nasiko-ee registry yank `. Browsing, searching, and downloading stay open to anyone who can reach the registry. Publish, yank, and edit require admin credentials. ## Connecting an MCP client The registry exposes an MCP server over Streamable HTTP, so MCP-capable clients can browse and act on it directly. | Tool | Auth | Purpose | | - | - | - | | `list_artifacts`, `search_artifacts`, `get_artifact`, `list_versions`, `get_version`, `list_facets`, `download_artifact` | none | Browse, filter, download | | `publish_artifact`, `yank_artifact` | admin | Publish or remove a version | ```sh theme={null} claude mcp add --transport http nasiko-registry https://registry.example.com/mcp \ --header "Authorization: Basic $(printf 'admin:secret' | base64)" ``` Reads need no credentials. Add the header only if the client should publish or yank. ## Deploying a registry ```sh theme={null} nasiko-ee registry init \ --host \ --domain registry.example.com \ --admin-password nasiko-ee registry status # registries you manage and their state nasiko-ee registry upgrade --tag # pull a new image and restart nasiko-ee registry logs --tail 50 ``` Pull and push access is governed by the credentials you configure for that deployment. `nasiko-ee activate ` saves pull credentials for a cluster consuming images from a gated registry. Where publishing fits into shipping an agent. What makes an agent a valid, discoverable artifact. Ready-to-deploy agents you can pull. Browse and publish from the registry's web app. # Sample agents Source: https://docs.nasiko.com/build/registry/sample-agents Curated example agents — what each demonstrates, and how to deploy one. Nasiko ships ready-to-deploy example agents: small, standalone A2A containers, each doing one thing. Read them as reference implementations or deploy them as-is. ## Domain and utility agents Each calls a real public API (no key required unless noted), written as a minimal [A2A](/build/a2a) example in Rust or Go. | Agent | What it demonstrates | | - | - | | `weather` | Current conditions and 3-day forecast via Open-Meteo | | `hackernews` | Browsing and searching Hacker News | | `books` | Searching Open Library | | `github` (Go) | Searching public repos and browsing contents | | `nutrition` | Nutrition facts lookup | | `finance-agent` | Exchange rates and crypto market data | | `legal-agent` | Full-text search across SEC EDGAR filings | | `hr-agent` | Public holidays and business-day calculations | | `paper` | Searching arXiv and Semantic Scholar | | `docs` | crates.io crate info, versions, and doc links | | `devops-agent` | GitHub repo metadata and CI/CD run history | | `repo-watch-agent` | Repo activity over a window, with automatic deep-dives on risky commits | | `currency-agent` | A converter with no LLM and no outbound HTTP — the simplest possible A2A agent | | `coding` | An inline ReAct loop over an OpenAI-compatible model, with tools confined to a sandboxed workspace | | `assistant-agent` | Discovers peer agents via the registry, delegates, and synthesizes — one agent calling others | ## Framework demos The same small task — answering a question from Wikipedia — implemented once per framework, showing that A2A and zero-code instrumentation work regardless of what you built with. | Agent | Framework | | - | - | | `claude-sdk` | Claude Agent SDK | | `langchain` | LangChain | | `google-adk` | Google Agent Development Kit | | `openai` | OpenAI Agents SDK | | `langgraph` | LangGraph — a currency converter instead, showing multi-turn dialogue and streaming | | `crewai` | CrewAI — image generation and editing instead | Use these when deciding which framework to build in. Each is a complete, deployable reference for wiring that framework to A2A. ## Reusable skill snippets Single-capability building blocks meant to be copied into an agent rather than deployed: `arxiv-search`, `http-request`, `tmdb-search`, `web-search`, `wikipedia-search`. ## Auto-deploying at startup `SEED_AGENTS` is a space-separated list of image references, read **once at server boot**: ```sh theme={null} SEED_AGENTS="nasiko/weather nasiko/finance-agent:v2" ``` On startup the Nasiko server registers new images, redeploys changed ones, and leaves unchanged ones alone. It runs as a background task, so it doesn't block readiness, and one image's failure doesn't stop the others. `SEED_AGENTS` is read once at process start, not watched. Editing it requires a control-plane restart to take effect. `SEED_FORCE_PULL` forces a redeploy of every listed image on the next boot even if its reference didn't change — useful after pushing a new build under the same tag. ## Deploying one yourself **Enterprise feature (partially).** `nasiko registry pull` assumes a reachable registry (typically the `nasiko-ee`-hosted artifact registry — see [artifact registry](/build/registry/overview)). `nasiko deploy` itself is real OSS functionality (pushes to the embedded `/v2/*` OCI registry) — you can deploy any local image with it, this particular pull step is just the registry-hosted shortcut. ```sh theme={null} nasiko registry pull nasiko/weather nasiko deploy nasiko/weather ``` See [Agent Development Lifecycle](/build/overview) for the full workflow and the [artifact registry guide](/build/registry/overview) for everything published beyond this set. ## Related Scaffold, run, and deploy your own agent. What makes an agent discoverable and routable. Browse and publish beyond the curated set. How a pulled image becomes a running container. # Routing engine Source: https://docs.nasiko.com/build/routing-engine How Nasiko picks an agent for a query, the knobs to tune, and how to inspect decisions. The routing engine is a three-stage pipeline that picks one agent from a candidate set. It makes one decision, then hands off — it's not an agentic loop. For several agents collaborating on one task, see [multi-agent workflows](/build/workflows). ## When routing runs This pipeline does **not** run on every chat message that omits an agent. Read this section before assuming what powers your unrouted chat traffic. Chat requests go to `POST /api/orchestrator/a2a`. What happens depends on `agent_id`: | Request | What runs | | - | - | | Explicit `agent_id` | Proxies straight to that container. No selection. | | No `agent_id`, or `"orchestrator"` | A multi-turn **orchestrator** — every accessible agent becomes a callable tool and an LLM decides across up to ten turns which to call. No shortlist or rerank. See [how Nasiko dispatches to agents](/build/a2a#how-nasiko-dispatches-to-agents). | The pipeline below is invoked in one place today: **auto-assigning an agent to a [MAF workflow](/build/workflows) step** that doesn't name one. Tuning it shapes MAF step assignment, not live chat routing. Full detail: [where the pipeline actually runs](/governance/flow-limits#where-the-pipeline-actually-runs). ## The three stages ```mermaid theme={null} flowchart LR Q(["User query"]) --> S1["① Shortlist
semantic similarity"] S1 --> S2["② Rerank
conversation context"] S2 --> S3["③ Select
LLM decision"] S3 --> A(["Chosen agent"]) ``` 1. **Shortlist** — ranks agents by semantic similarity between the query and each agent's description, keeping the top candidates. Skipped for small fleets (`ROUTER_SHORTLIST_THRESHOLD`) or if the embedding provider is unavailable; either way every agent advances. 2. **Rerank** — re-scores candidates using conversation context, so a follow-up is more likely to stay with the agent already handling the session. On failure, the shortlist order is kept. 3. **Select** — an LLM makes the final call. On failure, the top-ranked candidate is chosen and the decision is marked `fallback_used: true`. Every stage degrades gracefully — a query is never rejected because a routing sub-step had trouble. Decisions are logged asynchronously, off the response path. ## Tuning Environment variables on the Nasiko server: | Env var | Default | What it controls | | - | - | - | | `ROUTER_SHORTLIST_THRESHOLD` | `15` | Fleet size above which shortlisting runs at all | | `ROUTER_SHORTLIST_SIZE` | `10` | Max candidates kept for reranking | | `ROUTER_MODEL` | `gpt-4o` | Model for the selection stage | | `EMBEDDING_MODEL` | provider default | Embedding model for the shortlist stage | | `ROUTER_AGENT_TIMEOUT_SECS` | `60` | How long to wait on the chosen agent's response | These tune the engine's *own* decision-making, separate from which model an agent calls once selected — see [reusable LLM configs](#reusable-llm-configs). ## Inspecting decisions `GET /api/orchestrator/stats` returns aggregated stats from this pipeline's decisions — today, MAF step auto-assignments. One row per agent per day, newest and most-selected first, capped at 200 rows. ```sh theme={null} curl -H "Authorization: Bearer $TOKEN" https:///api/orchestrator/stats ``` ```json theme={null} { "data": [ { "agent_name": "weather-bot", "selection_count": 142, "successful_calls": 138, "failed_calls": 4, "avg_agent_latency_ms": "812.4", "avg_selection_latency_ms": "310.2", "avg_stage1_candidates": "6.0", "avg_stage2_candidates": "3.0", "date": "2026-07-30" } ], "total": 1 } ``` There's no CLI command for this yet — call the route directly. To see which agent handled a *live chat* query, look at its session and trace instead: `GET /api/observability/session/{id}` and `GET /api/observability/trace/{id}`. ## Reusable LLM configs Separate from engine tuning, you can control which LLM an *individual agent* calls, through a per-user library of named configs: ```sh theme={null} # available providers, models, price per 1M tokens nasiko llm-config providers # create a named config nasiko llm-config create --name support-cheap --provider openai --model gpt-4o-mini # attach it to an agent you own nasiko llm-config attach support-bot support-cheap # check what an agent will use, and where that resolved from nasiko llm-config get support-bot ``` A config carries a provider, model, optional fallback models, tuning (temperature, max tokens), and optionally your own API key. `--pin` locks an agent to one model — the engine still picks *which agent* answers, but that agent's LLM calls stay fixed. `nasiko model-registry ls` / `set` manage a platform-wide strength-level→model table (level 1 strongest, level 3 smallest) that a smart-routing config can draw from. Changing it requires superuser. Full command set: `nasiko llm-config --help`. Configure providers and per-level models in the web app. What your routing and model choices cost. What makes an agent discoverable and routable. Failure behavior and the flow guard. # Running and chatting locally Source: https://docs.nasiko.com/build/run-locally Build, run, and chat with your agent before it touches a cluster. An agent speaks A2A the moment it starts. You don't need a cluster, or even a connected CLI, to run it and hold a full conversation with it. ```sh theme={null} nasiko build # current directory nasiko build --tag my-agent:v2 # custom tag nasiko build ./path/to/agent ``` Reads the `Dockerfile` to build, and `AgentCard.json` for the `name:version` tag. ```sh theme={null} nasiko run # build + start on port 8000 nasiko run --port 8080 ``` Your agent is live at `http://localhost:8000`, speaking A2A over HTTP. ```sh Interactive theme={null} nasiko chat http://localhost:8000 ``` ```sh One-shot theme={null} nasiko chat http://localhost:8000 "What can you do?" ``` ```sh Full-screen TUI theme={null} nasiko chat http://localhost:8000 --tui ``` All three talk A2A directly to your container — no Nasiko server hop. ## Resuming a conversation Conversations are tracked by session, keyed to the A2A `contextId` your agent returns. ```sh theme={null} nasiko sessions # list local sessions nasiko chat http://localhost:8000 --session-id "..." # continue a conversation, one-shot nasiko chat http://localhost:8000 --resume --tui # continue a conversation, full-screen TUI nasiko history # show message history ``` `--resume` and `--session-id` are not interchangeable: `--resume` always opens the TUI (it fails outside a real terminal), `--session-id` sends a one-shot message into an existing session. Useful for iterating on multi-turn behavior — resume the same session after a code change instead of replaying every turn. ## Chatting once you're deployed The same command works against a deployed agent or the routing engine once you've [connected to a cluster](/build/deploy): ```sh theme={null} nasiko chat "hello" # the routing engine nasiko chat my-agent "hi" # a specific deployed agent nasiko chat --agent my-agent "hi" # same, explicit flag ``` A lone argument containing whitespace is the message, not the target. ## Next Push your agent to a cluster. The protocol behind every `chat` call. # Scaffolding a new agent Source: https://docs.nasiko.com/build/scaffold Generate an agent project from a template and manage its AgentCard.json. Every agent project is built around two files: `AgentCard.json` — the source of truth for the agent's identity — and a `Dockerfile`. `nasiko new` scaffolds both. ## Project structure ``` my-agent/ AgentCard.json # you manage this (or `nasiko card` generates it) Dockerfile # you manage this (or `nasiko new` scaffolds it) src/ # your agent source code .nasiko/agent.json # auto-created by `nasiko deploy` — don't edit, gitignore it ``` `AgentCard.json` carries: | Field | What it is | | - | - | | `name` | The agent's unique identifier on a cluster | | `version` | The image tag used for deploys, e.g. `0.1.0` | | `description`, `skills` | What the agent does and exposes — see [A2A agents](/build/a2a) | | `capabilities` | Streaming, push notifications, and other flags | | `url`, `protocolVersion` | The agent's endpoint and A2A version | `.nasiko/agent.json` is written on your first `nasiko deploy`. It links the directory to an agent ID on the cluster, so later deploys update that agent instead of creating a new one. ## Scaffold a project ```sh theme={null} nasiko new # interactive — pick a template nasiko new openai my-agent # from a named template nasiko new claude-sdk my-agent ``` With no arguments, `nasiko new` lists templates from the connected artifact registry (Nasiko's public registry is connected by default — see [artifact registry](/build/registry/overview)). Templates for OpenAI's Agents SDK, Anthropic's SDK, CrewAI, LangChain/LangGraph, and Google's ADK. A thin `main.rs` plus `tools.rs` for your domain logic, built against an A2A server implementation. Same shape as the Rust templates. Every template produces starter source that already implements the A2A contract. You fill in the skill logic. The directory name you pass (`my-agent` above) is not the agent's name — it's just where the files land. Templates ship with a fixed placeholder `name` already set in `AgentCard.json` (e.g. the `openai` template always scaffolds as `openai-research-agent`). Scaffold the same template twice under different directory names and `nasiko deploy`/`nasiko upload` will treat both as the *same* agent, silently updating one record instead of creating two. Edit `AgentCard.json`'s `name` field yourself (or run `nasiko card`) if you want a distinct agent. ## Generate or update the agent card ```sh theme={null} nasiko card "A code review agent that finds bugs in PRs" nasiko card # auto-detect from source nasiko card --dir ./my-agent ``` Connected to a cluster, `nasiko card` uses an LLM to read your source and produce the card. Disconnected, it falls back to interactive prompts. Re-run it whenever you add or change a skill. The platform reads the card at deploy time to populate the agent registry — it doesn't watch your source. ## Validate before you build ```sh theme={null} nasiko validate ``` Checks that `AgentCard.json` and `Dockerfile` exist and are well-formed. Run it after scaffolding and before your first deploy. ## Next Build the image and start chatting. What your agent must expose to be a valid A2A peer. # Versions and rollback Source: https://docs.nasiko.com/build/versions Immutable deploy versions, history, reupload, and rollback. Each deploy records an immutable version. Bump the version, redeploy, and you can roll back to an earlier one without rebuilding. ## Version on deploy The `version` field in `AgentCard.json` is the image tag — the deployed image is `name:version`. Pass `-v` to set it on the command, or `-y` to accept the suggested version without a prompt. ```bash theme={null} nasiko deploy . -v 0.2.0 nasiko deploy . -y nasiko push . -v 0.2.0 -y ``` Agent names are unique per cluster. Deploying the same name creates a new version on that agent. Re-pushing a version that is already in use is rejected. ## History, reupload, rollback ```bash theme={null} nasiko versions --name my-agent nasiko versions nasiko reupload --name my-agent nasiko reupload --name my-agent -v 0.3.0 --changelog "fix tool timeout" nasiko rollback --name my-agent nasiko rollback --name my-agent -t 0.2.0 --reason "bad prompt" ``` | Command | What it does | | - | - | | `versions` | Version history for a deployed agent | | `reupload` | Upload source again and rebuild. Version resolution: `--version` → `AgentCard.json` → `pyproject.toml` → `Cargo.toml` → server auto-patch | | `rollback` | Return the running agent to a previous version. Omit `-t` to roll back one version. `--reason` is stored in the audit log | API: `GET /api/agents/{id}/versions`, `POST /api/agents/{id}/rollback`, `PUT /api/agents/{id}/update` (reupload), `DELETE /api/agents/{id}/versions/{version}`. `nasiko restart` recreates the container from the **current** image and secrets. It does not change versions. Use `rollback` to change which version is current. ## Registry artifacts (Enterprise) **Enterprise.** This section describes the standalone artifact registry (`nasiko-ee`), not the built-in image registry at `/v2/*`. Open-source clusters keep images in that embedded registry; they do not host publish/yank. Each publish creates a new version row under that artifact's name. Versions are free-text, not enforced semver. "Latest" means most recently published and non-yanked. Every version starts at `preview`. An administrator can label it `stable` or `verified` — display only. Yanking hides a version from browse, list, and search. Anyone who pinned `owner/name/version` can still fetch it. ```bash theme={null} nasiko-ee registry publish --owner [--type] nasiko-ee registry yank nasiko registry search "code review" -t agent ``` See [Registry](/build/registry/overview). ## Next Push, upload, import. Logs, scale, writable storage. # MAF Source: https://docs.nasiko.com/build/workflows Chain multiple agents into a repeatable, trackable workflow. A routed chat request picks *one* agent. A **MAF (Multi-Agent Flow) workflow** is a named, ordered sequence of steps, each bound to an agent, where each step's output feeds the next and a final step synthesizes one answer. Use a workflow when a task needs several specialists chained — research, then analyze, then summarize. ## Define, generate, run ```mermaid theme={null} flowchart TB U(["Describe a workflow"]) --> G{"Generate a draft?
(optional)"} G -- "POST /api/maf/generate" --> D["Draft plan
agents + steps + output guidance"] G -- "write steps by hand" --> C D --> C["POST /api/maf/workflows
create the workflow"] C --> R["POST /api/maf/workflow/{id}/run"] R --> W["Background worker
runs each step in order,
feeding outputs forward"] W --> S["Final synthesized answer"] S --> P["GET /api/maf/workflow/result/{exec_id}"] ``` **Define** — `POST /api/maf/workflows` with a name, optional description, and steps. Each step needs a task description and either an explicit agent or nothing — unassigned steps are auto-assigned by the routing engine at creation time, scoped to agents you can access. **Generate a draft** (optional) — `POST /api/maf/generate` takes a natural-language description, looks at your registered agents, and returns a draft: name, description, output-synthesis guidance, and steps with agents assigned. Review it, then create it for real. Requires an LLM on the Nasiko server; returns `503` if none is configured. **Run** — `POST /api/maf/workflow/{id}/run` queues an execution and returns `202` with an `execution_id`. Poll `GET /api/maf/workflow/result/{exec_id}` for status and output. ### The same thing in the dashboard Everything above is available under **Orchestrator → Workflows** without writing a request. **Authoring a workflow is the access-control boundary.** Which agents a workflow reaches depends on who created it and which agents they named. Treat workflow creation as a meaningful permission, and review agent assignments in any workflow built programmatically. ## What a step carries At run time the platform expands your step's task description into a prompt — resolving placeholders from earlier steps' outputs — plus an "extraction goal" describing what to pull from the reply. Each step moves `pending → running → success` (or `failed`), with token usage and latency recorded. The workflow's `output_generation` guidance says how to turn all step outputs into one answer. Each step adds planning, prompt-filling, and extraction around the agent call, so a workflow execution uses noticeably more tokens than the same number of direct chat messages. Check per-step token numbers before scaling up. ## Reliability Runs are queued and picked up by a background worker, not executed inline. Failed steps retry up to `MAF_MAX_ATTEMPTS` (default `3`). Executions in flight during a server restart are picked back up. A queued execution lives in Redis until a worker claims it. Size Redis durability accordingly — see [backup and restore](/self-hosting/backup-and-restore). Each step lands as its own trace, so the same agent appearing in two steps has distinguishable token usage. See [observability](/dashboard/sessions-and-traces). ## Example Two steps — the first names an agent, the second is auto-assigned: ```sh theme={null} curl -X POST https:///api/maf/workflows \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "research-and-summarize", "steps": [ { "task_description": "Research recent news on topic X", "agent_id": "" }, { "task_description": "Summarize the research into 3 bullet points" } ] }' # -> { "data": { "id": "", "name": "research-and-summarize", ... } } curl -X POST https:///api/maf/workflow//run \ -H "Authorization: Bearer $TOKEN" # -> 202 { "execution_id": "", "execution_number": 1, "execution_count": 1 } curl https:///api/maf/workflow/result/ \ -H "Authorization: Bearer $TOKEN" # -> { "data": { "status": "success", "output": "...", "step_results": [ ... ] } } ``` The same calls are available as `nasiko maf` CLI commands — see [workflow CLI reference](/reference/cli/workflows). ## Routes | Method | Endpoint | Purpose | | - | - | - | | `GET` / `POST` | `/api/maf/workflows` | List your workflows / create one | | `POST` | `/api/maf/generate` | Draft a workflow from a description | | `GET`/`PUT`/`DELETE` | `/api/maf/workflow/{id}` | Read, edit, or remove | | `POST` | `/api/maf/workflow/{id}/run` | Queue an execution — `202` with an `execution_id` | | `GET` | `/api/maf/workflow/{id}/executions` | Run history for one workflow | | `GET` | `/api/maf/executions` | Every execution across your workflows | | `GET` | `/api/maf/execution/{id}` | One execution's status | | `GET` | `/api/maf/workflow/result/{exec_id}` | Poll for status and final output | ## Related The routing engine that auto-assigns unassigned steps. Watch a multi-agent run step by step. How per-step traces stay distinguishable. Building the agents a workflow calls. # Changelog Source: https://docs.nasiko.com/changelog What changed in Nasiko, most recent first. Customer-facing changes from the open-source tree. Dates are approximate. **Coding agents.** Discover, register, and report sessions for Claude Code, Codex, OpenCode, and the Cursor CLI. Route Claude Code, Codex, and OpenCode through the LLM router. Durable local telemetry queue; install bound to the cluster at install time. **TokenOps and Overview.** Landing Overview screen; spend timeseries, calendar, attributions, agent hours; cache-aware token accounting. **Compression.** Per-agent token-optimization switch; orchestrator tool-result and history compression. **HITL.** Inline approvals in `nasiko chat` and `maf run --wait`; `/api/hitl/*`. **Custom providers.** LiteLLM, vLLM, Ollama, Azure OpenAI, AWS Bedrock Converse, OpenRouter. **PACMS.** `nasiko budget` and `nasiko context-strategy`. **Versions.** `reupload`, `versions`, `rollback`, immutable image tags. **SCIM / SSO UI** in Settings (OIDC login and directory sync backends are Enterprise). **OSS cleanup.** Flutter control-plane UI removed; vanilla dashboard is the UI. **MCP gateway.** Catalog, connectors, OAuth, uploads, per-agent allow/ask/block, CLI `nasiko mcp`. **LLM configs.** Reusable library, attach/detach, model registry. **TokenOps APIs.** `finops/dashboard` and `insights`; Portkey price-book sync. **OIDC session handoff** in open-source; full SSO login in Enterprise. **MCP and MAF foundations.** Workflows CLI, default-deny tool permissions, per-user LLM configs. # Claude Code Source: https://docs.nasiko.com/coding-agents/claude-code Discover Claude Code, report its sessions, and route its model calls through Nasiko. Nasiko treats Claude Code as a coding harness. Discovery and session reporting use Claude Code's own hooks. Model routing uses Claude Code's own `apiKeyHelper` and `ANTHROPIC_BASE_URL`. No wrapper, no SDK in your code. | | | | - | - | | Binary | `claude` | | Config directory | `$CLAUDE_CONFIG_DIR` for routing; `~/.claude` for session reporting | | Session reporting | Yes (`Stop` hook, hook version v3) | | Model routing | Yes (Anthropic Messages protocol in, any provider out) | ## Discover and report ```bash theme={null} nasiko agents discover nasiko agents install claude nasiko agents install claude --no-content # tokens and tools only, no prompt text ``` Install writes a `Stop` command hook into `~/.claude/settings.json` and a launcher under `~/.claude/`. Existing hooks stay. Uninstall removes only the Nasiko entries. Session reporting always uses `~/.claude`, even when `$CLAUDE_CONFIG_DIR` is set. After install, use `claude` the way you always do. Each completed turn is queued under `~/.nasiko/integrations/queue/` and delivered in the background. See [Session reporting](/coding-agents/session-reporting). ## Route models ```bash theme={null} nasiko llm-config create --name my-openai --provider openai --model gpt-4o \ --api-key-secret OPENAI_API_KEY --secret-value "$OPENAI_API_KEY" nasiko connect claude --config my-openai nasiko status claude ``` `connect claude` edits `~/.claude/settings.json`: * `apiKeyHelper` runs `nasiko __claude-token`, which mints a one-hour JWT from your cluster. * `env.ANTHROPIC_BASE_URL` points at the cluster. Claude Code keeps sending Anthropic-format `/v1/messages` requests; Nasiko translates and routes them. Claude Code sends the JWT as `x-api-key`. The router accepts that header and `Authorization: Bearer`. Connection state is stored in `~/.nasiko/integrations/claude.json`. `~/.claude/settings.json` is per-user and global. Connecting Claude Code affects every `claude` process on the machine. If routing is configured under `$CLAUDE_CONFIG_DIR`, that directory is what `connect` and `disconnect` edit. Reporting still uses `~/.claude`. ### One-shot, no settings change ```bash theme={null} nasiko claude --agent --config my-openai -- -p "Explain this diff" ``` Environment is injected for that process only. `--agent` is required. ## Disconnect and uninstall ```bash theme={null} nasiko disconnect claude # restore provider settings; --force if claude is running nasiko agents uninstall claude # remove the reporting hook ``` The two are independent. Disconnecting leaves reporting in place. ## Identity in Nasiko Registration creates a coding-agent identity named `-claude-code`, labelled **Claude Code (``)** in the dashboard. Reported sessions and routed calls are both attributed to it. ## Troubleshooting | Symptom | What to check | | - | - | | `DETECTED` is `-` | `claude` is not on `PATH` and `~/.claude` does not exist. | | Routing fails with authentication error | `AGENT_JWT_SECRET` is unset on the server, or `nasiko auth status` shows an expired login. | | Settings look connected but calls still go to Anthropic | Restart Claude Code so it reloads `settings.json`. Confirm `ANTHROPIC_BASE_URL` in `~/.claude/settings.json`. | | Turns never appear | Run `nasiko agents sync`. Check `~/.nasiko/integrations/report.log` and `rejected/`. | More: [Troubleshooting](/coding-agents/troubleshooting) and [Route models](/coding-agents/routing). # Codex Source: https://docs.nasiko.com/coding-agents/codex Discover Codex, report its sessions, and route its model calls through Nasiko. Nasiko treats Codex as a coding harness. Discovery and session reporting use Codex hooks. Model routing adds a Nasiko provider to Codex's `config.toml`. No wrapper, no SDK in your code. | | | | - | - | | Binary | `codex` | | Config directory | `$CODEX_HOME`, else `~/.codex` | | Session reporting | Yes (`UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop`; hook version v2) | | Model routing | Yes (OpenAI Responses protocol in, any provider out) | ## Discover and report ```bash theme={null} nasiko agents discover nasiko agents install codex nasiko agents install codex --no-content ``` Install writes Nasiko command hooks into Codex's hooks config and a launcher under the Codex config directory. Existing hooks stay. Open Codex, run `/hooks`, and trust the new Nasiko command hooks. Codex will not fire them until you do. Codex turns arrive across several hook events. Nasiko assembles them under `~/.nasiko/integrations/events/` and queues a turn once it is complete. See [Session reporting](/coding-agents/session-reporting). ## Route models ```bash theme={null} nasiko llm-config create --name my-anthropic --provider anthropic --model claude-sonnet-4-5 \ --api-key-secret ANTHROPIC_API_KEY --secret-value "$ANTHROPIC_API_KEY" nasiko connect codex --config my-anthropic nasiko status codex ``` `connect codex` edits `{CODEX_HOME}/config.toml`: * `model_provider = "nasiko"` * `[model_providers.nasiko]` with `wire_api = "responses"` and `base_url = {cluster}/v1` * Command-based auth that runs `nasiko __coding-agent-token codex` Codex talks OpenAI Responses (`POST /v1/responses`). Nasiko translates and routes to the provider in your config. Codex refreshes the JWT on a timer rather than minting one per request. Connection state is stored in `~/.nasiko/integrations/codex-router.json`. ## Disconnect and uninstall ```bash theme={null} nasiko disconnect codex # restore config.toml; --force if Codex is running nasiko agents uninstall codex # remove reporting hooks ``` The two are independent. ## Identity in Nasiko Registration creates a coding-agent identity named `-codex`, labelled **Codex (``)** in the dashboard. ## Troubleshooting | Symptom | What to check | | - | - | | `DETECTED` is `-` | `codex` is not on `PATH` and `$CODEX_HOME` / `~/.codex` does not exist. | | Hooks never fire | Trust them with `/hooks` inside Codex. | | Routing fails with authentication error | `AGENT_JWT_SECRET` is unset, or login expired. | | Turns never appear | Run `nasiko agents sync`. Partial turns sit in `~/.nasiko/integrations/events/` until `Stop`. | More: [Troubleshooting](/coding-agents/troubleshooting) and [Route models](/coding-agents/routing). # Cursor CLI Source: https://docs.nasiko.com/coding-agents/cursor Discover the Cursor CLI and report its sessions to Nasiko. Nasiko treats the Cursor CLI (`cursor-agent`) as a coding harness. Discovery and session reporting use Cursor's hook events. **Model routing is not available** for Cursor: `nasiko connect cursor` is not a command. Developers keep using Cursor's own model settings. | | | | - | - | | Binary | `cursor-agent` | | Config directory | `$CURSOR_CONFIG_DIR`, else `$XDG_CONFIG_HOME/cursor`, else `~/.cursor` | | Session reporting | Yes (`beforeSubmitPrompt`, `afterAgentResponse`, `stop`, `preToolUse`, `postToolUse`, `postToolUseFailure`; hook version v2) | | Model routing | No | ## Discover and report ```bash theme={null} nasiko agents discover nasiko agents install cursor nasiko agents install cursor --no-content ``` Install writes Nasiko command hooks into `hooks.json` in the Cursor config directory. Existing hooks stay. Uninstall removes only the Nasiko entries. Cursor turns arrive across several hook events. Nasiko assembles them under `~/.nasiko/integrations/events/` and queues a turn once it is complete. See [Session reporting](/coding-agents/session-reporting). After install, keep using `cursor-agent` the way you always do. Completed turns show up in **Sessions** and in [TokenOps](/tokenops/dashboard) under the **Cursor (``)** agent. ## What you cannot do ```bash theme={null} nasiko connect cursor # not supported nasiko status cursor # not a routing target nasiko disconnect cursor # not supported ``` Routing targets are `claude`, `codex`, and `opencode` only. Cursor spend is still attributed when reporting is installed, using whatever model Cursor itself called. ## Identity in Nasiko Registration creates a coding-agent identity named `-cursor`, labelled **Cursor (``)** in the dashboard. The agent page has no **Configure** or **Logs** tab — nothing runs on Nasiko for a coding agent. ## Troubleshooting | Symptom | What to check | | - | - | | `DETECTED` is `-` | `cursor-agent` is not on `PATH` and no Cursor config directory exists. The Cursor **editor** is not the same as the Cursor CLI. | | Turns never appear | Confirm hooks in the Cursor `hooks.json`. Run `nasiko agents sync`. Check `~/.nasiko/integrations/report.log`. | | Want to change Cursor's model | Change it in Cursor. Nasiko does not intercept Cursor model calls. | More: [Troubleshooting](/coding-agents/troubleshooting). # Discover and register coding agents Source: https://docs.nasiko.com/coding-agents/discover List the coding harnesses on a machine, register them with your Nasiko cluster, and turn session reporting on or off. ## Discover what is installed ```sh theme={null} nasiko agents discover ``` Discovery reads the local machine, prints one row per supported harness, and exits. It changes no harness settings, needs no login, and sends nothing to your Nasiko cluster. ```text theme={null} AGENT DETECTED CONNECTED VERSION CONFIG ---------------------------------------------------------------------------------------- claude yes yes v3 ~/.claude codex yes no - ~/.codex opencode - no - ~/.config/opencode cursor yes no - ~/.cursor Install session reporting: nasiko agents install ``` | Column | Meaning | | - | - | | `AGENT` | The ID you pass to other commands: `claude`, `codex`, `opencode`, or `cursor`. | | `DETECTED` | `yes` when the harness binary is on your `PATH` or its config directory exists. `-` when neither is found. | | `CONNECTED` | `yes` when session reporting is installed and current. This column is about reporting only; check model routing with `nasiko status `. | | `VERSION` | The installed reporting hook version, such as `v3`. `-` means not installed. `v2 -> v3` means the hook is out of date. `v3 (state v2)` means the hook and the saved install record disagree. In both of the last two cases, run `nasiko agents install ` again. | | `CONFIG` | The harness config directory Nasiko reads and writes. | A config directory with no binary still counts as detected: it means the harness was used on this machine before. ## Install session reporting ```sh theme={null} nasiko agents install claude nasiko agents install codex --no-content ``` `install` needs an active cluster and a current login (`nasiko auth login`). It does three things: 1. Registers the harness with your Nasiko cluster, or finds the existing registration. 2. Adds a reporting hook (or plugin, for OpenCode) to the harness config directory. 3. Saves a local install record that binds reporting to the cluster and user that are active right now. `--no-content` reports tokens, latency, models, and tool metadata but leaves out prompts, responses, session titles, and tool inputs and outputs. See [what is captured](/coding-agents/session-reporting#what-is-captured). Some harnesses need one more step: * **Codex:** open Codex, run `/hooks`, and trust the new Nasiko command hooks. * **OpenCode:** restart OpenCode so it loads the new plugin. Then start a new session in the harness. Completed turns show up in **Sessions** in the dashboard, and in `nasiko sessions` and `nasiko observe sessions`. `install` fails if the harness is not detected on the machine, if you are not logged in, or if the harness config file it needs to edit is not valid JSON. ## Automatic install on login These commands also set up session reporting for you: * `nasiko auth login` * `nasiko connect ` * `nasiko use ` After the command succeeds, Nasiko installs reporting for every detected harness that has **no install record yet**, with content capture on. It runs only when the active cluster has a current login; otherwise it prints that setup was skipped. One harness failing does not stop the others — you get a warning and a `nasiko agents install ` command to retry. Automatic install never rebinds a harness that is already installed. If you switch clusters, a harness you installed earlier keeps reporting to its original cluster. To move it, run `nasiko agents install ` while the new cluster is active. Model routing commands such as `nasiko connect claude` do not trigger automatic install. ## Cluster binding Each install is bound to the cluster name, cluster URL, and user that were active when you ran it. Every turn is queued for that destination and no other. Nasiko never reroutes queued turns to a different cluster or user. To change the binding, or to switch `--no-content` on or off, run `nasiko agents install ` again with the cluster you want active. Turns queued before that stay bound to their original destination. See [cluster or user changes](/coding-agents/session-reporting#cluster-or-user-changes). ## What registration creates Registering a harness creates one coding-agent identity per harness per user on your Nasiko server. Registering again returns the same identity. | Harness | Identity name | Dashboard label | | - | - | - | | Claude Code | `-claude-code` | Claude Code (``) | | Codex | `-codex` | Codex (``) | | OpenCode | `-opencode` | OpenCode (``) | | Cursor CLI | `-cursor` | Cursor (``) | The identity is owned by you and tagged `local` and `coding-agent`. Reported sessions and routed model calls are both attributed to it, so cost rolls up by harness and developer. [Model routing](/coding-agents/routing) reuses the same identity. ## Where coding agents appear in the dashboard * **Agents** lists each registered coding agent under its label, such as **Claude Code ([alice@example.com](mailto:alice@example.com))**, alongside your deployed agents. Nothing runs on Nasiko for a coding agent, so its agent page has no **Configure** or **Logs** tab. * **Sessions** lists reported coding sessions next to your other chats. A coding-agent session opens read-only, with the note "This is a recorded coding-agent conversation. Continue it in the original coding agent." * **Observability** shows the same sessions with traces, tokens, and latency. * **TokenOps** shows their cost. See [TokenOps dashboard](/tokenops/dashboard). From the CLI, `nasiko agents ls` lists registered coding agents with the same label. ## Uninstall session reporting ```sh theme={null} nasiko agents uninstall claude ``` `uninstall` removes the Nasiko hook entries from the harness config, deletes the hook script, and removes the local install record. It leaves everything else in the harness config alone. The coding-agent identity and its past sessions stay on the server. Turns already in the local queue are still delivered. `uninstall` does not change model routing; use `nasiko disconnect ` for that. For every flag, see the [coding agents CLI reference](/reference/cli/coding-agents). # OpenCode Source: https://docs.nasiko.com/coding-agents/opencode Discover OpenCode, report its sessions, and route its model calls through Nasiko. Nasiko treats OpenCode as a coding harness. Discovery and session reporting use an OpenCode plugin. Model routing installs a second plugin that registers Nasiko as a provider. No wrapper, no SDK in your code. | | | | - | - | | Binary | `opencode` | | Config directory | `$OPENCODE_CONFIG_DIR`, else `$XDG_CONFIG_HOME/opencode`, else `~/.config/opencode` | | Session reporting | Yes (`session.idle` plugin, hook version v4) | | Model routing | Yes (`nasiko/router` provider for new sessions) | ## Discover and report ```bash theme={null} nasiko agents discover nasiko agents install opencode nasiko agents install opencode --no-content ``` Install writes `plugins/nasiko-session-report.js` into the OpenCode config directory. Restart OpenCode after install so it loads the plugin. The plugin waits at most 8 seconds for the reporting hook and swallows errors, so OpenCode is never blocked if Nasiko is down. See [Session reporting](/coding-agents/session-reporting). ## Route models ```bash theme={null} nasiko llm-config create --name my-openai --provider openai --model gpt-4o \ --api-key-secret OPENAI_API_KEY --secret-value "$OPENAI_API_KEY" nasiko connect opencode --config my-openai nasiko status opencode ``` `connect opencode` installs `plugins/nasiko-llm-router.js`, registers a `nasiko` provider, and makes `nasiko/router` the default model for **new** OpenCode sessions. The plugin calls `nasiko __coding-agent-token opencode` for a JWT. If the helper times out or returns invalid JSON, OpenCode gets an error — the plugin never forges a token. Connection state is stored in `~/.nasiko/integrations/opencode-router.json`. OpenCode's config is per-user and global. Connecting it affects every OpenCode process on the machine. OpenCode fixes a session's model at creation time in its own database. Resuming an existing session will **not** route it. Start a new session, or pick **Nasiko Router** explicitly. ## Disconnect and uninstall ```bash theme={null} nasiko disconnect opencode # remove the router plugin; --force if OpenCode is running nasiko agents uninstall opencode # remove the reporting plugin ``` Restart OpenCode after either command so plugins reload. ## Identity in Nasiko Registration creates a coding-agent identity named `-opencode`, labelled **OpenCode (``)** in the dashboard. ## Troubleshooting | Symptom | What to check | | - | - | | `DETECTED` is `-` | `opencode` is not on `PATH` and no OpenCode config directory exists. | | Plugin never loads | Restart OpenCode. Confirm `plugins/nasiko-session-report.js` (and the router plugin, if connected) exist in the config directory. | | Old sessions still hit the vendor | Expected. OpenCode pins the model per session. Start a new one. | | Routing helper errors | `nasiko auth status`, then `AGENT_JWT_SECRET` on the server. | More: [Troubleshooting](/coding-agents/troubleshooting) and [Route models](/coding-agents/routing). # Coding agents Source: https://docs.nasiko.com/coding-agents/overview Find the coding agents already running on your machines, see what each one costs, and route them to the models you choose. Find the coding agents already running. See what each one costs, by harness, model and team. Reach any model from any harness. One command on a laptop, the same command across an estate — and nothing changes about how anyone works. Nasiko works with the coding harnesses your developers already use: Claude Code, Codex, OpenCode, and the Cursor CLI. It does three things for them: * **Finds them.** `nasiko agents discover` lists the supported harnesses on a machine and whether each one reports to Nasiko. * **Measures them.** Session reporting records every completed turn — tokens, model, latency, tool calls, and optionally the conversation — so you can see what each harness costs in [TokenOps](/tokenops/dashboard). * **Routes them.** Model routing points a harness at your Nasiko cluster instead of its vendor, so its calls go to the model and provider you choose. ## Non-invasive and non-intrusive **Non-invasive:** the harness you already run keeps running unchanged. Nasiko adds no wrapper around it and no SDK to your code. Reporting uses the harness's own hook or plugin mechanism. Routing uses the harness's own provider settings. **Non-intrusive:** setup is one command per harness. After that, developers start `claude`, `codex`, `opencode`, or `cursor-agent` the same way they always have. Discovery on its own is read-only. It changes no harness settings, needs no account, and sends nothing off the machine. ## Supported harnesses | Harness | Binary | Config directory | Session reporting | Model routing | | - | - | - | - | - | | [Claude Code](/coding-agents/claude-code) | `claude` | `~/.claude` | Yes | Yes | | [Codex](/coding-agents/codex) | `codex` | `$CODEX_HOME`, else `~/.codex` | Yes | Yes | | [OpenCode](/coding-agents/opencode) | `opencode` | `$OPENCODE_CONFIG_DIR`, else `$XDG_CONFIG_HOME/opencode`, else `~/.config/opencode` | Yes | Yes | | [Cursor CLI](/coding-agents/cursor) | `cursor-agent` | `$CURSOR_CONFIG_DIR`, else `$XDG_CONFIG_HOME/cursor`, else `~/.cursor` | Yes | No | Cursor CLI supports session reporting only. Model routing is available for Claude Code, Codex, and OpenCode. For Claude Code, model routing honors `$CLAUDE_CONFIG_DIR` when it is set. Session reporting always uses `~/.claude`. A harness counts as detected when its binary is on your `PATH` or its config directory exists. Detection is local to the machine you run the command on. ## Two independent opt-ins Session reporting and model routing are separate. You can turn on either one, both, or neither, per harness. | | Session reporting | Model routing | | - | - | - | | What it does | Records completed turns and sends them to your Nasiko cluster | Sends the harness's model calls through the Nasiko LLM router | | Turn on | `nasiko agents install ` (or automatically at login) | `nasiko connect ` | | Turn off | `nasiko agents uninstall ` | `nasiko disconnect ` | | Check | `nasiko agents discover` | `nasiko status ` | | Harnesses | Claude Code, Codex, OpenCode, Cursor CLI | Claude Code, Codex, OpenCode | | If Nasiko is unreachable | Fails open: turns queue locally and the harness keeps working | Fails closed: the harness gets an error and never falls back to the vendor | Both use the same coding-agent identity on the server: one per harness per developer. That identity is what ties a routed call and a reported session to the same developer and harness. * [Discover and register coding agents](/coding-agents/discover) * [Session reporting](/coding-agents/session-reporting) * [Model routing](/coding-agents/routing) ## Get started Install the CLI, run discovery, and see your first reported session. Every flag is listed in the [coding agents CLI reference](/reference/cli/coding-agents). # Route models Source: https://docs.nasiko.com/coding-agents/routing Point a coding harness at Nasiko so its model calls go to the provider and model you choose. Harnesses arrive tied to a vendor: Claude Code to Anthropic, Codex to OpenAI. Routing gives that choice back to you. Register a provider and key once, then point the harness at your Nasiko cluster. The harness keeps speaking its own protocol; Nasiko selects the outbound provider. Routing is available for Claude Code, Codex, and OpenCode. The Cursor CLI supports [session reporting](/coding-agents/session-reporting) only. Routing and session reporting are independent. You can route without reporting, or report without routing. See [Coding agents](/coding-agents/overview). ## Connect a harness ```bash theme={null} nasiko llm-config create --name my-openai --provider openai --model gpt-4o \ --api-key-secret OPENAI_API_KEY --secret-value "$OPENAI_API_KEY" nasiko connect claude --config my-openai nasiko status claude ``` `connect` needs an active cluster and a current login. It: 1. Reuses the coding-agent identity created by [registration](/coding-agents/discover#what-registration-creates), or registers one if you haven't installed reporting yet. 2. Attaches the LLM config you named with `--config`. Omit `--config` to use your default config, or `--agent` to reuse an existing coding-agent identity by name or UUID. 3. Writes the harness's own provider settings so new sessions call Nasiko instead of the vendor. Inbound protocol and outbound provider are decoupled. `nasiko connect claude --config my-openai` sends Claude Code's Anthropic-format traffic to OpenAI. Claude Code's `~/.claude/settings.json` and OpenCode's config are per-user, global files. Connecting a harness affects every process of that harness on the machine, not just the current project. ## What changes on your machine | Harness | Files written | How credentials are obtained | | - | - | - | | Claude Code | `~/.claude/settings.json`: `apiKeyHelper` → `nasiko __claude-token`; `env.ANTHROPIC_BASE_URL` → your cluster URL | Claude Code runs the helper; Nasiko mints a one-hour JWT from `POST /api/agents/{id}/llm-token`. Claude Code sends it as `x-api-key`. | | Codex | `$CODEX_HOME/config.toml` (or `~/.codex/config.toml`): `model_provider = "nasiko"` and `[model_providers.nasiko]` with `wire_api = "responses"` and `base_url = {cluster}/v1` | Command-based auth runs `nasiko __coding-agent-token codex`. Codex refreshes on a timer rather than minting a token per request. | | OpenCode | `{opencode-config}/plugins/nasiko-llm-router.js`, plus a `nasiko` provider whose default model is `nasiko/router` | The plugin calls the same credential helper. Failures return an error to OpenCode; they never forge a token. | Connection state is saved under `~/.nasiko/integrations/`: | Path | Harness | | - | - | | `claude.json` | Claude Code | | `codex-router.json` | Codex | | `opencode-router.json` | OpenCode | The `__claude-token` and `__coding-agent-token` helpers are internal. Don't call them yourself. ## How a call is routed ```mermaid theme={null} flowchart LR Harness(["Harness"]) -->|"its own protocol"| Nasiko["Nasiko LLM router"] Nasiko -->|"provider you chose"| Provider["Model provider"] Nasiko -.->|"tokens, cost"| TokenOps["TokenOps"] ``` The harness talks to Nasiko at `/v1/messages` (Claude Code), `/v1/responses` (Codex), or the OpenCode plugin's equivalent. The router translates the inbound format, selects the model from the attached LLM config, and calls the provider. Spend is attributed to the coding-agent identity, so it shows up next to reported sessions in [TokenOps](/tokenops/dashboard). If the config's `model` is unset, the router falls through the [model registry](/models/model-registry) tiers and then the cluster default. ## Failure behavior Routing credentials **fail closed**. If Nasiko cannot issue a JWT — the cluster is unreachable, your login expired, `AGENT_JWT_SECRET` is unset on the server, or the helper times out — the harness gets an error. It never falls back to calling the vendor with the original key. That is the opposite of [session reporting](/coding-agents/session-reporting), which fails open: turns queue locally and the harness keeps working. Self-hosting: set `AGENT_JWT_SECRET` on the server or every routed call is rejected with `401`. See [Server configuration](/self-hosting/configuration). ## One-shot Claude Code To run Claude Code through Nasiko for a single invocation, without changing `~/.claude/settings.json`: ```bash theme={null} nasiko claude --agent --config my-openai -- ``` `--agent` is required. Everything after `--` is handed to the `claude` binary. Environment for that process is injected for the duration of the command. ## Disconnect ```bash theme={null} nasiko disconnect claude nasiko disconnect codex --force ``` `disconnect` restores the harness settings Nasiko changed and deletes the local routing state file. It does **not** uninstall session reporting — use `nasiko agents uninstall ` for that. If the harness is still running, `disconnect` refuses unless you pass `--force`. Stop the harness first when you can, so it doesn't keep using a credential helper that is no longer installed. ## Check status ```bash theme={null} nasiko status # cluster health nasiko status claude # routing binding for one harness nasiko status codex nasiko status opencode ``` Per-harness status shows whether routing is connected, which cluster and LLM config it uses, and the coding-agent identity. For every flag, see the [coding agents CLI reference](/reference/cli/coding-agents). For LLM configs, see [LLM configs](/models/llm-configs). # Session reporting Source: https://docs.nasiko.com/coding-agents/session-reporting What Nasiko records for each coding-agent turn, how turns are queued and delivered, and where they show up. Session reporting records each completed turn in a coding harness and delivers it to your Nasiko cluster. Turn it on with `nasiko agents install `, or let `nasiko auth login` do it for you. See [Discover and register coding agents](/coding-agents/discover). A **turn** is one prompt from the developer and everything the harness did to answer it: its model calls, its tool calls, and its final response. A **session** is the harness's own conversation, made of one or more turns. ## What is captured Nasiko reports a turn once it is complete — it has a final response and at least one model call. | Captured | Always | Only with content capture | | - | - | - | | Turn start and end time | Yes | | | Each model call: provider, model, input, output, cache-read and cache-write tokens, start and end time | Yes | | | Each tool call: name, status, start and end time, duration | Yes | | | Prompt and final response text | | Yes | | Session title (Claude Code) | | Yes | | Tool call arguments, output, and error text | | Yes | Content capture is on by default. Install with `--no-content` to turn it off: ```sh theme={null} nasiko agents install claude --no-content ``` Each text field is capped at 1 MiB and truncated past that. ## Hooks used by each harness Every harness calls the same Nasiko script, `hooks/nasiko-session-report.sh`, inside its config directory. | Harness | Mechanism | Events | | - | - | - | | Claude Code | Command hook in `~/.claude/settings.json` | `Stop` | | Codex | Command hooks in `hooks.json` in the Codex config directory | `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop` | | OpenCode | Plugin `plugins/nasiko-session-report.js` in the OpenCode config directory | `session.idle` | | Cursor CLI | Command hooks in `hooks.json` in the Cursor config directory | `beforeSubmitPrompt`, `afterAgentResponse`, `stop`, `preToolUse`, `postToolUse`, `postToolUseFailure` | Install adds the Nasiko entries next to any hooks you already have and leaves yours untouched. Uninstall removes only the Nasiko entries. ## The harness never waits on Nasiko Reporting fails open for the harness: * The hook script always exits successfully. If anything goes wrong, the error goes to `~/.nasiko/integrations/report.log`, never to the harness. * The hook only reads the turn and writes it to a local queue. It does no network work, and it finishes within the 10-second hook timeout Nasiko registers. * Delivery happens afterwards, in a separate background `nasiko agents sync` process. * The OpenCode plugin waits at most 8 seconds for the hook and swallows any error. If your Nasiko cluster is down, slow, or unreachable, the harness carries on and turns wait in the queue. ## Local state All reporting state lives under `~/.nasiko/integrations/`. The queue and rejected directories are readable only by your user. | Path | Contents | | - | - | | `config.json` | One install record per harness: its identity name, whether content is captured, the hook version, and the cluster and user it is bound to | | `queue/` | Turns waiting for delivery, grouped by destination | | `rejected/` | Turns that will not be delivered, each with its last error | | `watermarks/` | Per-session record of which turns have already been queued, so each turn is queued once | | `events/` | Partial turns for Codex and Cursor CLI, whose turns arrive across several hook events | | `report.log` | Hook diagnostics | ## Delivery, retries, and quarantine After queueing a turn, the hook starts `nasiko agents sync` in the background. You can also run it yourself: ```sh theme={null} nasiko agents sync ``` `sync` sends queued turns to `POST /api/telemetry/coding-agent/events/batch` on the bound cluster, up to 100 per request, and keeps going until the queue is empty or about five minutes have passed. * **Accepted or duplicate:** the server stored the turn, or already had it. Nasiko removes it from the queue. Replaying a turn is safe. * **Network or server error:** Nasiko retries with backoff (1, 2, 4, then 8 seconds). After 5 failed attempts the turn moves to `rejected/`. * **Rejected by the server:** the turn moves to `rejected/` straight away. This happens when the event is invalid, or when the server has no active coding-agent identity for that harness and user — for example, because the registration was deleted. Files in `rejected/` are not retried and are safe to delete. Read the `last_error` field in each file to see why it was rejected. ## Cluster or user changes Turns are bound to the cluster and user active at install time. Before delivering, `sync` checks that binding against your current CLI config. It refuses to deliver, and keeps the turns queued, when: * the bound cluster is no longer configured, * the bound cluster's URL has changed, * you are not logged in to the bound cluster, or your login has expired, or * you are logged in to the bound cluster as a different user. Refused turns stay in the queue and don't use up any retry attempts. Fix the cause — for example, run `nasiko use ` and `nasiko auth login` as the same user — then run `nasiko agents sync`. To report new turns somewhere else, run `nasiko agents install ` while the new cluster is active. That changes the binding for future turns only. Turns already queued are never sent to a different cluster or user. ## Where sessions appear With content capture on, each accepted turn becomes a message pair in a chat session right away, with its tokens, model, and cost: * **Sessions** in the dashboard. Coding-agent sessions open read-only. * `nasiko sessions` lists them, and `nasiko history ` prints the turns. With `--no-content`, the session is created, but no prompts or responses are stored, so no messages show in the transcript. Token counts, models, and tool metadata are still exported as traces. Traces, and the cost views built on them in **Observability** and [TokenOps](/tokenops/dashboard), also need the server to export coding-agent telemetry. Set `CODING_AGENT_OTLP_ENDPOINT` on the Nasiko server to an OTLP/HTTP endpoint. The bundled Docker Compose stack points it at its OpenTelemetry Collector. When it is unset, sessions still appear, but no coding-agent traces reach the trace store. See [Server configuration](/self-hosting/configuration). For every flag, see the [coding agents CLI reference](/reference/cli/coding-agents). # Troubleshooting coding agents Source: https://docs.nasiko.com/coding-agents/troubleshooting Discovery, reporting, routing, and cluster-binding problems. Work through the matching section. Commands and log paths are the same on every harness. ## Discovery **`nasiko agents discover` is missing from `--help`.** Reinstall with `cargo install --path cli --force`. Every build reports version `0.1.0`, so `--version` cannot tell you whether you have the latest commands. **A harness shows `DETECTED` as `-`.** The binary is not on `PATH` and the config directory does not exist. For Cursor, the binary is `cursor-agent`, not the Cursor editor. **`CONNECTED` is `yes` but I never ran `install`.** `nasiko auth login`, `nasiko connect `, and `nasiko use` install reporting automatically for every detected harness that has no install record yet. **Version column says `v2 -> v3` or `v3 (state v2)`.** The hook is out of date, or the hook and the saved install record disagree. Run `nasiko agents install ` again. ## Session reporting **Turns never appear in Sessions or TokenOps.** 1. Confirm **Connected** is `yes` with `nasiko agents discover`. 2. Finish a turn in the harness (Claude Code `Stop`, OpenCode `session.idle`, Codex/Cursor `Stop` / `stop`). 3. Run `nasiko agents sync`. 4. Read `~/.nasiko/integrations/report.log`. 5. Files in `~/.nasiko/integrations/rejected/` will not be retried. The `last_error` field says why. 6. Codex: trust the hooks with `/hooks`. OpenCode: restart so the plugin loads. **Sessions appear, but traces, tokens, and cost stay empty.** Set `CODING_AGENT_OTLP_ENDPOINT` on the Nasiko server to an OTLP/HTTP endpoint (Compose sets `http://otel-collector:4318`). Without it, ingested turns still create sessions, but no coding-agent traces reach Tempo. See [Server configuration](/self-hosting/configuration). **`--no-content` sessions have no transcript.** Expected. Token counts, models, and tool metadata are still exported as traces when OTLP is configured. **I switched clusters and old turns vanished.** They didn't. Each install is bound to the cluster and user active at install time. `sync` refuses to deliver if that cluster's URL changed, you are logged in as a different user, or the login expired. Re-login to the original cluster, or reinstall while the new cluster is active — that rebinds *future* turns only. Queued turns stay bound to their original destination. **The harness feels slow or blocked.** Reporting is designed not to do that. The hook always exits successfully, does no network work, and finishes within a 10-second timeout. Delivery is a background `nasiko agents sync`. If a harness is waiting, it is not waiting on Nasiko reporting. ## Model routing **`nasiko connect cursor` fails.** Cursor CLI has no model routing. Reporting only. See [Cursor CLI](/coding-agents/cursor). **Connect succeeds, calls still hit the vendor.** * Restart the harness so it reloads settings or plugins. * OpenCode: start a **new** session. Existing sessions keep the model they were created with. * Claude Code: confirm `ANTHROPIC_BASE_URL` in `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR`). * Codex: confirm `model_provider = "nasiko"` in `config.toml`. **Authentication error on every routed call.** * `nasiko auth status` — login must be current on the cluster routing is bound to. * `AGENT_JWT_SECRET` must be set on the server. Empty means every router request is rejected with `401`. `nasiko up` does not set it; Docker Compose leaves it commented in `.env.example`. * Routing **fails closed**. The harness will not fall back to the vendor. That is intentional. **`disconnect` refuses because the agent is running.** Stop the harness, or pass `--force`. **I disconnected but reporting is still on.** Expected. `disconnect` only restores routing settings. Use `nasiko agents uninstall ` to remove reporting. ## Cluster and login **`install` says I am not logged in.** `nasiko connect ` then `nasiko auth login`. Automatic install is skipped when there is no current login. **`nasiko up` routing or traces don't work.** `nasiko up` does not set `AGENT_JWT_SECRET`, `TEMPO_URL`, `LOKI_URL`, or `CODING_AGENT_OTLP_ENDPOINT`. Export them or use Docker Compose. See [Install and connect](/install#troubleshooting-nasiko-up). ## Logs and state to inspect | Path | What it is | | - | - | | `~/.nasiko/integrations/config.json` | Install records and cluster binding | | `~/.nasiko/integrations/queue/` | Turns waiting for delivery | | `~/.nasiko/integrations/rejected/` | Turns that will not be delivered | | `~/.nasiko/integrations/report.log` | Hook diagnostics | | `~/.nasiko/config.json` | Clusters, active cluster, login token | `nasiko agents sync` is always safe to re-run. Replaying a turn the server already stored is a no-op. # Agents Source: https://docs.nasiko.com/dashboard/agents Browse the catalog, deploy agents, manage the ones you've deployed, and watch builds. Five screens cover agents: **Agents** (the catalog), **Your Agents** (what you've deployed), **Add Agent** (three ways to register one), the agent card, and **Builds**. To script the same lifecycle instead, see [Agent Development Lifecycle](/build/overview). ## Agents — the catalog Lists every agent you can reach, running or not. * **Search** filters by name, description, or tag as you type * **Category** filters by tag, built from every tag in the catalog Each card shows a name, status badge, description, and up to three tags, with **Details** (the agent card) and **Chat** ([direct chat](/dashboard/chat)). ## Your Agents — what you've deployed Lists the containers you manage. The stats bar shows **running**, **stopped**, and **error** counts — click one to filter, click again to clear. Search filters by name or image; sort by **Name**, **Status**, or **Version**. Each card shows the name, status badge, and `name:version`. Actions depend on state: | State | Actions | | - | - | | Running | **Restart**, **Stop**, **Delete** | | Stopped | **Deploy**, **Delete** | **Deploy** opens a modal for environment variables — import them from saved secrets, or set new ones (which are saved to the agent's secrets for future deploys). **Delete** confirms, then stops the container and removes it from the registry. An agent in an error state shows an inline link to its logs. ## Add Agent Three methods: | Method | Requirement | What happens | | - | - | - | | **Import from GitHub** | GitHub connected; `AgentCard.json` at repo root | Pick a repo from a searchable list, then **Import** clones and builds it | | **Upload a code package** | `.zip` with `skill.json` at root | Uploads immediately and builds | | **Import from OCI registry** | An artifact reference, e.g. `nasiko/my-agent:v1.0` | Pulls the pre-built image | Upload and GitHub both build the container image first — watch progress in [Builds](#builds). Failures show an error in place of the redirect. ## The agent card Opens from **Details** in the catalog or an agent's name in Your Agents. The top shows name, avatar, version, tags, and description. Header actions: **Restart** and **Stop** when running, **Delete**, and **Start session** for [direct chat](/dashboard/chat). **Overview** * **Skills** — each registered skill; one with a sample query opens a chat with it pre-filled * **Quick performance** — recent trace count, total cost, p50/p99 latency * **Agent details** — provider, project and docs URLs, ID, version, protocol, transport, default I/O * **Capabilities** — streaming, push notifications, state history, and current chat reachability * **Access Control** — which agents this one may call, and who can access it (public badge, or user/team/department/agent grants) **Settings** — read-only image, port, status, and replica count. **Logs** — recent container output. Choose 50, 100, or 500 lines; **Follow** keeps the view at the latest line. ## Builds Every image build triggered from the browser, in a searchable paginated table. Each row: short build ID, version tag, image reference, status badge (**success**, **building**, **failed**, **queued**, **cancelled**), source repo link for GitHub builds, and start time. Opening a build shows a summary, a numbered step breakdown with durations when steps are recorded, and log output. Logs stream live while a build is still in **building**. ## Related The same lifecycle from the CLI. What your container must expose. Talk to an agent directly or via the orchestrator. Traces, logs, and cost over time. # Artifact registry Source: https://docs.nasiko.com/dashboard/artifact-registry Browse, inspect, and publish agents, skills, and other artifacts in the registry's web app. The artifact registry has its own web app, separate from the control-plane dashboard — its own navigation (**Artifacts** and **Publish**) and its own sign-in, scoped to that registry instance. For the CLI and API side, see [Artifact registry](/build/registry/overview). For a curated set of artifacts, see [Sample agents](/build/registry/sample-agents). Open whatever address your organization gave you for the registry — this page describes the screens, not a fixed hostname. ## Browsing artifacts The landing page is a card grid. Type tabs narrow it to **All**, **Agents**, **Skills**, **MCP**, or **Miscellaneous** (which appears only when something falls outside the three headline types). Each tab shows a count. Search is live and debounced. **Framework** and **Format** facet dropdowns appear only when the catalog has two or more distinct values to filter on. Each card shows: * Type icon, owner, and name * A type badge, plus a status badge when the latest version isn't `stable` (`preview`, `verified`, `yanked`) * Description, or "No description provided" * Up to three tags, with a "+N" badge for more * Latest version, version count, size, and last updated An empty result suggests adjusting filters. An empty catalog hints at pushing an artifact with `oras`, `docker`, or `nasiko-ee`. ## Artifact detail * Owner, name, type, status badges, description * License, framework, packaging format, last updated * Stat tiles: latest version, version count, size, first published * A ready-to-copy pull command — `docker pull` for Docker-packaged artifacts, `oras pull` otherwise — plus `nasiko skill add ` for a skill, or the OCI reference to import an agent into the cluster * **Overview** tab — tags and a details grid (type, format, license, framework, first published, latest digest, custom metadata), plus a copyable `.well-known/agent.json` link for agents * **Versions** tab — every version with digest, size, age, and a lifecycle status badge ## Publishing The **Publish** screen takes owner, name, version, type (**Agent**, **Skill**, or **Tool**), framework, description, tags, license, and a `.tar.gz` archive. Success or failure is reported inline. This screen is hidden from the registry's navigation — reach it via a direct link to `publish.html`. ## Admin sign-in and edits Some detail-page actions require a registry admin sign-in — a separate username/password form, distinct from your Nasiko login. Once signed in, an admin can: * **Edit details** — description, tags, license, framework, custom metadata, and the artifact's type (useful when a push landed as `unknown`) * **Change a version's lifecycle status** between `preview`, `stable`, and `verified` * **Yank a version** — a confirmed action that hides it from search and installs Browsing, searching, and downloading stay open to anyone who can reach the registry. The CLI and API side — publishing, pulling, the MCP interface. Ready-to-deploy agents in the registry. The control-plane app this registry is separate from. # Chat Source: https://docs.nasiko.com/dashboard/chat Let Nasiko pick an agent, or talk to one directly. The dashboard has two chat surfaces. **Orchestrator** answers a question without you naming an agent. **Chat** is a direct conversation with one agent. Both are saved to [Sessions](/dashboard/sessions-and-traces). ## Orchestrator The home screen for chat. Type a question; Nasiko decides which agent answers. From the input box you can: * Type a message and submit it * Record voice input — click the microphone or press **F8** / **Alt+R**; audio is transcribed before sending * Attach a file **Recent agents** below the input links straight into a direct conversation with a running agent. ### Sending a message 1. A **Working…** indicator shows step-by-step activity as the platform consults agents. Expand it to watch. 2. The response streams in with full markdown — lists, tables, and syntax-highlighted code, each block with a **Copy** button. 3. A toolbar appears with **Copy** for the whole reply and a **view trace** link when a trace was produced. 4. **Continue in chat** opens the full Chat screen for the same session. Your message goes to the **orchestrator** — a multi-turn ReAct loop over every agent you can access. It is not the three-stage routing engine. The routing engine auto-assigns agents to [workflow](/build/workflows) steps. See [how Nasiko dispatches to agents](/build/a2a). For a fixed sequence of steps, use a [workflow](/build/workflows) instead. ## Direct chat A conversation with one agent, no routing. You get here from **Chat** on an agent's detail page, **Start** on an agent card, or a card in Orchestrator's **Recent agents**. The header shows the agent's name, a **Running** indicator, and a link to its card. A fresh conversation shows suggested prompt chips. Sending works the same as Orchestrator — same input box, same streaming display. The difference: your message is proxied straight to that agent's container. Each reply also gets a **Retry** action. Opening a chat with a `session_id` in the URL reloads that conversation instead of starting fresh. ## Sessions Every conversation is saved the moment you send the first message, whether it came from Orchestrator or direct chat. Revisit history and drill into traces in [Sessions](/dashboard/sessions-and-traces). **Recent sessions** in the sidebar lists past conversations by title. Opening one restores the full transcript with an **Ask a follow-up** box, so you continue where you left off rather than starting over. # MCP gateway Source: https://docs.nasiko.com/dashboard/mcp-gateway Register MCP servers, upload your own, and control which tools each agent may call. The **MCP gateway** screen gives agents access to real tools — Gmail, Slack, GitHub, an internal API, or a server you wrote. Everything in the [CLI and API guides](/build/mcp/overview) is available here as a form. The page has three sections: **Connectors**, **My uploads** (once you have one), and **Agent access**. Two header buttons: * **Register connector** — point at an MCP server already running somewhere * **Upload MCP server** — hand over source and let the platform build, harden, and run it ## Connectors Lists everything available to you, including what teammates shared. A shared connector shows *shared by* and the owner's name. | Column | What it shows | | - | - | | **Connector** | Display name, with internal name underneath | | **URL** | The server's address | | **Transport** | Usually `streamable_http` | | **Auth** | No auth, API key, Basic, OAuth 2.1, or URL param | | **Tools** | How many tools it exposes | | **Status** | Active, Inactive, or — for uploads — Building or Failed | Click a row or the gear icon to open its detail panel. The trash icon appears only on connectors you own and asks to confirm, since deleting one removes its tools from every agent using it. The sidebar splits the list three ways — **All**, **Created by you**, and **Shared with me** — and the **All** / **Connected** tabs filter each of those down to what you've connected a credential to. Each card shows the server's name, its tool count, and a connected badge. ## Registering an external server **Name** is the internal identifier (`github`). **Display name** is what people see (`GitHub`) and is optional. Paste the URL (e.g. `https://mcp.example.com/mcp`) and click **Probe**. The platform contacts the server, works out what authentication it expects, and selects the matching auth type with a hint explaining what it found. Probing is optional. A failed probe usually means the URL is wrong or unreachable. | Auth type | Fields | | - | - | | **No auth** | none | | **API key (bearer)** | Credential header — defaults to `Authorization` | | **Basic auth** | Username and password | | **OAuth 2.1** | Client ID and secret — leave blank if the server supports dynamic client registration | | **URL parameter** | The parameter name, e.g. `api_key` | Add an optional description and click **Register**. The connector appears immediately. Custom MCP servers must be reachable at a public address. URLs resolving to private, loopback, link-local, or cloud-metadata addresses are rejected — checked at registration and on every connection. ### Connecting your credential Registering doesn't sign you in. The connector's detail panel has a **Credential** section: * Enter your API key or token and click **Save** (or **Replace**). If the platform stores it but can't verify it against the server, it tells you. * **Remove** clears a stored credential. The field never shows a saved value — credentials are write-only here. OAuth 2.1 connectors get an **OAuth 2.1** section instead. **Authorize** opens your provider's consent screen in a popup; the panel then shows **Authorized** and the token expiry. **Revoke** disconnects. Everyone using a shared connector connects with **their own** credential or OAuth grant. Sharing never hands over the owner's login. ## Uploading your own MCP server **Upload MCP server** has two tabs: * **Upload zip** — name, optional version tag (defaults to `v1`), and a `.zip` of your source * **From GitHub** — name, version tag, and a repository URL Your source needs a `Dockerfile` at its root, and your server should read `$PORT` and serve MCP over Streamable HTTP at `/mcp`. Full contract: [Deploy your own MCP server](/build/mcp/deploy-server). **Queue build** returns the new connector's ID. The build runs in the background — close the modal and track it under **My uploads**. ### My uploads Each row shows the connector's name, status (**Deploying**, **Active**, **Failed**), any error message, and the resolved endpoint. The terminal icon opens **Build logs** — the first place to look when a build fails. Uploaded servers run third-party code, so they get extra hardening and network isolation. See [Deploy your own MCP server](/build/mcp/deploy-server). ## Toolkits A catalog of pre-built integrations — Airtable, Asana, Bitbucket, ClickUp, Confluence, Discord, Dropbox, GitHub, GitLab, Gmail, Google Calendar, Google Drive, and more — each with its tool count and description. **All**, **Connected**, and **Available to connect** filter the grid; the **+** on a card connects it, and a tick marks the ones already connected. ## Agent access Controls which connectors an agent may use, and which of their tools. Pick an agent in the search box. You then get a table of every connector available to it: * **Enabled** turns a connector on or off for that agent. Changes save on toggle — a failed save snaps the switch back and says why. * **Tools** expands an inline editor listing every tool with its description and an **Allow** / **Deny** dropdown. Set what you want restricted, then **Save rules**. Nothing needs configuring to *grant* access. A connector you can reach is usable by your agents by default. These controls only restrict. A connector that hasn't reported tools yet is usually still starting up. This screen sets Allow and Deny only. The API also supports an **ask** stance and glob patterns covering whole tool families — see [per-agent tool permissions](/governance/tool-permissions). ## Related Connectors, delegation tokens, and the request flow. The same flow from the CLI and API. What your source must provide. The full permission model. # Dashboard overview Source: https://docs.nasiko.com/dashboard/overview The Nasiko web app — Overview, TokenOps, sessions, agents, routing, and settings. The dashboard ships inside the Nasiko server. Open your cluster URL — nothing extra to install. It reads the same data the CLI and API do. ## Signing in Sign in with a username and password at your cluster URL. GitHub, Google, and Microsoft buttons appear only if your organization has enabled them. **Enterprise.** GitHub OAuth is wired in the open-source server. Microsoft/Google SSO buttons need the OIDC login route, which is `nasiko-ee` only — see [SSO and SCIM](/governance/sso-and-scim). Sessions are stored in a secure cookie. When a session expires, the app returns you to the login screen. ## Navigation (open-source) The rail, left to right: | Screen | Path | What it is | | - | - | - | | **Overview** | `/` | Fleet health and spend. See [TokenOps dashboard](/tokenops/dashboard) | | **Orchestrator** | `/orchestrator` | Chat without naming an agent. [Chat](/dashboard/chat) | | **Agents** | `/agents` | Catalog, Your Agents, Add Agent, Builds. [Agents](/dashboard/agents) | | **Sessions** | `/chats` | Transcripts, including coding-agent sessions. [Sessions](/dashboard/sessions-and-traces) | | **Observability** | `/sessions` | Traces, tokens, latency over the same rows | | **MCP gateway** | `/mcp` | Connectors and toolkits. [MCP](/dashboard/mcp-gateway) | | **LLM router** | `/llm-router` | Providers and reasoning-level configs. [LLM router](/models/llm-router) | | **TokenOps** | `/tokenops` | Cost view. [TokenOps](/tokenops/dashboard) | | **Settings** | `/settings` | General, flow limits, registry, SSO. [Settings](/dashboard/settings) | Also in the module trees, not all on the rail: **Workflows** and **Executions** ([Workflows](/dashboard/workflows)), **Set up CLI**, **Flows**, **Resources**, **Secrets**. **Enterprise.** **Departments**, **Teams**, **Users** (full org hierarchy), **Team Access**, **Agent Runtime**, and **Group Mappings** appear in `nasiko-ee` only. Open-source **Users** and access grants still exist without that hierarchy. ## Artifact registry **Enterprise.** The artifact registry is a standalone `nasiko-ee` service. Open-source ships the embedded image registry at `/v2/*` and a client (`nasiko registry connect/search/list`) that can point at an Enterprise registry if you have one. ## Screens Orchestrator or a named agent. Catalog, deploy, manage. MAF pipelines and executions. Connectors and per-agent tools. Transcripts and traces. Secrets, SSO, flow limits. # Observability Source: https://docs.nasiko.com/dashboard/sessions-and-traces Review past conversations, trace every agent hop, and follow a multi-agent flow. Three screens cover what happened: **Sessions**, **Session Trace**, and **Flows**. Token spend lives on the [TokenOps dashboard](/tokenops/dashboard). | Term | What it is | | - | - | | **Session** | One query, grouped by its conversation context — a row in the session list | | **Trace** | Every hop behind that query: the agent's LLM calls plus any agent-to-agent calls | | **Flow** | The same hop chain shown as a step-by-step timeline | Session Trace and Flow Detail show much the same data from two angles. ## Sessions Your past conversations, from Orchestrator or direct [Chat](/dashboard/chat). Each row shows: * The agent's name and initial avatar, or **Orchestrator** if no single agent handled it * A message count and a preview of the last message * Time since last active, grouped under **Today**, **Yesterday**, **This Week**, **Older** Search filters by agent name or message text. **New Chat** starts fresh; the trash icon removes a session. Clicking a session reopens the thread in Chat. A finished assistant reply carries a trace icon next to **Copy** — that opens its full trace. ## Session trace Every span behind a single query, in order, including nested agent-to-agent calls. The stats row shows **Spans**, **Duration**, **Tokens** (input + output), **Cost** when available, and the **Session** ID. The span list is indented to show nesting. Each span shows its name, model for LLM calls, token count, and duration; a red dot marks an error. Click a span for its kind, span ID, status, start and end times, model, and input/output token split. Traces take a few seconds to index. Opening one immediately after a conversation may say it isn't available yet — refresh. ## Flows Every multi-agent interaction, as a searchable sortable table: | Column | Detail | | - | - | | **Flow ID** | Links to the detail page | | **Query** | What started the flow | | **Agent** | The originating agent | | **Status** | Completed, running, failed, or timeout | | **Calls** | Agent invocations in the flow | | **Duration**, **Started** | Timing | Search filters by agent name or query text. ### Flow detail Summary cards for **Status**, **Root Agent**, **Duration**, **Started**, **Steps**, and **Query**, then a **Trace** timeline. Each step shows: * A status badge * The agent it called, with a preview of what was sent (hover for the full text) * A bar sized and positioned to show when it ran relative to the whole flow — the slow hop is visible at a glance * Its duration Designing a fixed multi-step sequence rather than reviewing one that ran? See [multi-agent workflows](/build/workflows). ## Tokens and cost Token spend has its own screen — see [TokenOps](/tokenops/dashboard) for per-agent cost, operations, agent hours, and latency by month, with CSV export. Cost also appears inline: a session trace's stats row shows that query's token count and dollar cost, so you can attribute one expensive answer without leaving the trace. ## API Everything these screens show is available under `/api/observability/*`: | Method | Endpoint | Purpose | | - | - | - | | `GET` | `session/list` | Sessions for an agent, paginated by time range | | `GET` | `session/{id}` | Full drill-down for one session | | `GET` | `trace/{id}` | One trace, every span | | `GET` | `span/{trace_id}/{span_id}` | One span's detail | | `GET` | `agent/{id}/stats` | Aggregate call, latency, and success stats | | `GET` | `finops/dashboard` | Cost dashboard data — backs [TokenOps](/tokenops/dashboard) | | `POST` | `finops/insights` | LLM-generated narrative over the cost data | | `GET` | `agents/{ref}/logs`, `agents/{ref}/logs/stream` | Log lines, with a streaming variant | ## Configuration Agents are traced with no code changes — every deployment gets OpenTelemetry configuration injected into its spec, so instrumentation starts at boot and `traceparent` propagates across agent-to-agent hops automatically. If you self-host, these are the knobs: | Env var | Purpose | | - | - | | `OTEL_EXPORTER_OTLP_ENDPOINT` | Where agents and the Nasiko server export traces | | `OTEL_*` (protocol, headers, sample ratio) | Exporter tuning, injected into every deployment | | `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` | Whether prompt and completion *text* is captured. Off still gives token metrics without raw content. | | `TEMPO_URL` | Trace query endpoint | | `LOKI_URL` | Log query endpoint | ## Related Where sessions and traces come from. Browse and manage deployed agents. Spend per agent, month by month. Design a fixed multi-step sequence. # Administration Source: https://docs.nasiko.com/dashboard/settings Manage agent secrets, platform settings, users, teams, departments, and access. **Secrets** and **Settings** are available to everyone with a login. The organization screens appear only once your role includes org or infrastructure management. For the role model, see [access control](/governance/access-control/overview). ## Secrets Lists an agent's credentials — API keys, connection strings, tokens — as key/value pairs the agent reads from its container environment. Each row shows the **Key** and creation date. The **Value** is always masked, in the table and while typing. Secrets are write-only from the UI: you can set and overwrite them, never read one back. The screen is titled **API Secrets & Credentials**; with nothing stored yet it shows **No secrets configured**. **Add secret** takes a **Secret name** (uppercase, numbers, underscores — `MY_API_KEY` style) and a **Secret value**, then **Save**. **Delete** removes one after confirmation. Saving a secret updates what's stored. It doesn't push the value into a running container — that happens on the next deploy or restart. See [managing agent secrets](/governance/secrets/agent-secrets) and [secret management](/governance/secrets/overview). ## Settings Three tabs of platform-wide configuration: | Tab | Contains | | - | - | | **General** | Instance name, default chat model, max output tokens per request | | **Models** | Anthropic API key for Claude routing, optional OpenAI key | | **Registry** | OCI registry URL, username, and password for pulling agent images | API keys and the registry password use masked fields — stored encrypted, never displayed back. Click **Save Changes** to apply. ## Organization and access screens **Enterprise feature.** Everything from here to "Doing this from the CLI" — Departments, Teams, Team Access, Agent Runtime (cluster provisioning), Group Mappings, and the org-wide Access Control rollup — is `nasiko-ee` functionality with no OSS equivalent. The plain **Users** screen has a partial OSS basis (basic account create/list/deactivate) but without the role hierarchy or team/department placement described below. | Screen | Who sees it | What it does | | - | - | - | | **Access Control** | Org administrators | Org-wide stats, health warnings, placement shortcut for unassigned members | | **Users** | Org administrators | Create, edit, deactivate, and delete accounts; assign roles and placement | | **Departments** | Team leads and above view; org admins manage | Browse departments and rollups | | **Teams** | Team leads and above view; org admins manage | Browse teams and rollups | | **Team Access** | Team leads, department managers, admins | Adjust role and placement within your own team or department | | **Agent Runtime** | Administrators | View and manage compute clusters | | **Group Mappings** | Top-level administrators | Map identity-provider groups to roles, teams, departments | Roles are `admin`, `department_manager`, `team_lead`, `team_member`, and `member`. The separate **superuser** flag unlocks several of these screens regardless of role — see [access control](/governance/access-control/overview). ### Access Control An org-wide dashboard: counts of admins, departments, teams, users, and agents, then warning cards for anything needing attention (users with no team or department, for example). Below, a **Departments** table rolls up each department's manager, team, member, and agent counts, and **Unassigned Members** lists users with no placement. The folder icon next to an unassigned user opens **Assign department / team** — pick a department, optionally narrow to a team, save. Leaving both empty clears their placement. Without the role to use this screen, it shows a plain error rather than data. ### Users Every account, with name/email, role, department/team, active status, and last login. Filter by role, department, or status; search matches name, username, or email. **Unassigned** and **Admins** are the same table pre-filtered — users with no placement, and accounts holding the admin role. * **Add User** — username, email, optional display name, role. No password: the platform generates a one-time **access key** and **access secret**, shown once in the dialog with copy buttons. The secret is the user's login password and can't be shown again. * **Edit** — username, email, display name, or a new password (blank keeps the current one) * **Assign department/team** (folder icon) — the same placement dialog as above * **Activate / Deactivate** — toggle sign-in * **Delete** — permanently remove the account * **Sync Azure AD Directory** — trigger a directory sync; a toast reports how many departments, teams, and users were created Without administrator access, this screen loads but says full user management requires it, and points you to Team Access. ### Departments and Teams Roster screens: a searchable table (name, manager or lead, team or member counts, agent count) plus stats. Every team belongs to a department, so creating one requires picking a department. Team leads and above can browse. Creating or editing is reserved for top-level administrators. ### Team Access The scoped version of Users. Shows only your own team — or your whole department, for a department manager — with **Edit Access** per person to change their role and, for admins and department managers, reassign their team. You can only grant a role below your own. A team lead can hand out team member or team lead, never department manager or admin. ### Agent Runtime The compute clusters backing your agents: provider (DigitalOcean, AWS, Azure), region, node size, and status from provisioning through ready to destroyed. Open a live provisioning log per cluster, or **Destroy Cluster** once it's no longer transitional. ### Group Mappings Connects identity-provider security groups to Nasiko roles. Each mapping ties an external group's object ID to a role, a department, a team, or any combination — at least one is required. New logins are placed by the highest-ranked matching mapping. **Sync Azure AD Directory** is available here too. ## Doing this from the CLI * [User and org management](/governance/access-control/users-and-teams) — creating users, teams, departments * [Managing agent secrets](/governance/secrets/agent-secrets) — secrets from the command line * [Access control reference](/governance/access-control/reference) — the full model behind roles, grants, and agent-to-agent access # Workflows Source: https://docs.nasiko.com/dashboard/workflows The dashboard screens for multi-agent workflows and their executions. **Workflows** (`/workflows`) and **Executions** (`/executions`) are the dashboard for [MAF](/build/workflows): named pipelines of agent steps. They sit under the **Orchestrator** module, next to live orchestrator chat. ## Workflows `/workflows` lists every workflow you can see: name, step count, updated time. **New workflow** opens `/workflow-new`. Opening a row loads `/workflow` with the step list, assigned agents, and **Run**. Creating a workflow in the UI is the same model as: ```bash theme={null} nasiko maf workflow create --name research-and-summarize \ --step "Research recent news on topic X" --agent research-agent \ --step "Summarize into 3 bullets" --agent - ``` `-` (and omitting an agent in the UI) lets the [routing engine](/build/routing-engine) assign that step. **Generate a draft** calls `POST /api/maf/generate`: describe the workflow in plain language, review the suggested steps, then save. Needs a model configured on the cluster; returns `503` if none is. Soft-delete from the workflow page. Executions already queued keep running. ## Executions `/executions` lists runs across workflows: status, started time, workflow name. Open one for step-level status, output, and cost (TokenOps can also attribute by workflow). ```bash theme={null} nasiko maf workflow run research-and-summarize --wait nasiko maf execution list nasiko maf execution result ``` `--wait` polls until the run finishes and prompts for any [HITL pause](/governance/approvals) inline. ## Related The workflow model, CLI, and API. Live ReAct over accessible agents, without a pre-defined pipeline. # User → agent access Source: https://docs.nasiko.com/governance/access-control/agent-access How ownership, the public flag, and grants decide who can chat with, update, or delete an agent. Every check against an agent asks one of two questions: 1. **Can this user reach the agent?** — chat with it, view it in a catalog, list its details. 2. **Can this user change or destroy it?** — update its config, delete it, view or rotate its secrets. Only the agent's **owner** or a **superuser** can do the second. Publishing an agent or granting a department access to it hands out no management rights. A soft-deleted agent is denied to everyone except through direct database recovery. No ownership, grant, or public flag overrides a deletion. ## What grants view and chat access A user can reach an agent if **any** of these is true: | Condition | Description | | - | - | | Ownership | The user created the agent | | Team ownership | The agent is assigned to a team the user belongs to | | `is_public` | Anyone on the platform can reach it | | Explicit grant | A grant names the user, their team, their department, or `*` | Grants are additive — an agent can stay privately owned and still be reachable by a whole department. `is_public` extends to anyone the platform's routing surfaces reach; an organization-wide grant is scoped to your authenticated users. **Enterprise feature.** "Team ownership" and team/department-scoped grants require `nasiko-ee`'s org hierarchy. The open-source edition's access model has only three conditions: ownership, `is_public`, and a per-user grant — there's no team or department concept to grant against. ## Managing grants **Enterprise feature.** Every command in this section (and "Managing an agent" below) is `nasiko-ee`. There's no equivalent in the plain `nasiko` CLI. ```bash theme={null} # Grant a user, a team, and a department in one call nasiko-ee access grant --user alice --team platform-eng --dept engineering # Make the agent public nasiko-ee access grant --public # Revoke one user, leaving team/department grants intact nasiko-ee access revoke --user alice # Make the agent private again nasiko-ee access revoke --public ``` `--user`, `--team`, and `--dept` accept multiple values and combine in one call. An agent can also be granted to *another agent*. This controls whether a human acting through that agent's context can reach this one — not whether the two agents may call each other programmatically. That's the [agent-to-agent allowlist](/governance/access-control/reference). Inspect current access: ```bash theme={null} nasiko-ee access ls # resolved users + agent grants nasiko-ee access visibility # is_public + every raw grant by type nasiko-ee access list-users # every user with access nasiko-ee access dept-grants # departments with a grant nasiko-ee access team-grants # teams with a grant nasiko-ee access accessible-agents # agents this agent can reach nasiko-ee access my-agents # agents you can reach nasiko-ee access user-agents # agents a user can reach (admin only) ``` ### Example: share with one team, private otherwise ```bash theme={null} nasiko-ee access grant a1b2c3d4 --team platform-eng nasiko-ee access visibility a1b2c3d4 ``` ``` Agent 'a1b2c3d4' visibility: Public: no user grants (0): (none) team grants (1): department grants (0): (none) agent grants (0): (none) ``` The agent stays private, but every `platform-eng` member can chat with it. ## Managing an agent These require ownership or superuser — view/chat access is not enough: ```bash theme={null} nasiko-ee access rename "new-name" nasiko-ee access owner nasiko-ee access org --team platform-eng --dept engineering ``` Managing secrets follows the same rule, plus the `team_lead` threshold from the [overview](/governance/access-control/overview). See [secret management](/governance/secrets/overview). ## Related * [Access control overview](/governance/access-control/overview) * [User and org management](/governance/access-control/users-and-teams) * [MCP access](/governance/tool-permissions) — which external tools an agent may call * [Secret management](/governance/secrets/overview) # Access control overview Source: https://docs.nasiko.com/governance/access-control/overview Who can chat with, manage, and deploy agents — and which agents may call each other. Nasiko has two independent access-control mechanisms, checked separately: 1. **User → agent** — which people can view, chat with, update, delete, or manage an agent's secrets. 2. **Agent → agent** — which agents may call which other agents. Neither implies the other. A department can chat with a research agent while that agent's ability to call a billing agent stays locked to an explicit allowlist. Create users, assign roles, build teams and departments. Ownership, the public flag, and explicit grants. Per-agent connector and tool permissions. Full technical reference. ## Roles **Enterprise feature.** The ranked role tiers below (`admin` through `member`) are enforced by `nasiko-ee`'s auth layer. The open-source edition has no role hierarchy — every authenticated user passes the equivalent of every capability check below by default (deploy, manage secrets, manage users are all unrestricted in OSS). If you're running plain `nasiko`, skip this section. Every user has exactly one role. Each tier includes everything below it. | Role | Scope | | - | - | | `admin` | Full platform administration | | `department_manager` | A department's teams, agents, and members | | `team_lead` | A team's agents and secrets | | `team_member` | Contributor on a team | | `member` | Baseline user, no team | A role sets *what kind* of action a user may attempt. Whether they can act on a *specific* agent is decided by [user → agent access](/governance/access-control/agent-access). | Capability | Minimum role | | - | - | | Deploy and run agents | `member` | | Manage an agent's secrets | `team_lead` | | Manage platform users | `admin` | **Role is not superuser.** Superuser is a separate flag. It bypasses organization-visibility scoping and unlocks platform-wide routes: creating users, creating departments and teams, OIDC group mappings, directory sync. Setting `role` to `admin` does not grant it — see [user and org management](/governance/access-control/users-and-teams). **Enterprise feature.** "Creating departments and teams," "OIDC group mappings," and "directory sync" above are all `nasiko-ee` only. ## Where each mechanism applies | Mechanism | Governs | See | | - | - | - | | User → agent | Chat, view, update, delete, secrets | [User → agent access](/governance/access-control/agent-access) | | Agent → agent | Which agents an agent may invoke | [Platform reference](/governance/access-control/reference) | | Per-agent MCP tools | Which tools an agent may call | [MCP access](/governance/tool-permissions) | Agent-to-agent calls are **default-deny**. Both the caller and the target need an explicit allowlist row. If a workflow between two agents fails, check for a missing allowlist entry first. # Access control reference Source: https://docs.nasiko.com/governance/access-control/reference How the two ACL layers are enforced, the exact capability thresholds, and org-scoped visibility. The [access control guide](/governance/access-control/overview) covers the configuration model. This page covers where each check runs, the exact thresholds, and visibility scoping. ## Two layers, checked in different places ### User → agent Every route touching a specific agent — proxying a chat request, updating, deleting, reading or rotating secrets — resolves access the same way: ownership, the public flag, or an explicit grant. A soft-deleted agent is denied unconditionally. **Enterprise feature.** Team/department ownership and grants require `nasiko-ee`'s org hierarchy — the open-source edition's `can_access_agent` check has only owner, `is_public`, and a per-user `agent_grants` row. Mutating operations (update, delete, secrets, ACL changes) use a **stricter** check: neither a public flag nor an invoke-only grant confers the right to modify or destroy an agent. Only ownership or superuser does. The distinction matters because view access is granted far more often than manage access — conflating them would let a public flag double as a management grant. ### Agent → agent Whenever an authorized agent calls *another* agent, a separate check applies: is this hop allowed? Governed by an allowlist of explicit caller/target pairs. **Agent-to-agent access is default-deny, unconditionally.** An agent with no allowlist entries cannot call any other agent. There's no "unrestricted until the first rule" state. If you deploy an agent that calls another as part of its logic, add that pairing first or the call fails. The check runs immediately before the target is invoked, in the same pass as the [flow guard's](/governance/flow-limits#the-flow-guard) cascade limits. A rejection from either aborts the call before it reaches the target container. The allowlist rows live in the `agent_acl` table. In the open-source edition there's no bundled endpoint for managing them yet — the corresponding grants router is defined but not mounted on the OSS server. `nasiko-ee` mounts its own richer version at `/api/agents/{id}/grants/agents[/{target_agent_id}]`. **Enterprise feature.** Managing this allowlist over the API is `nasiko-ee` only today. Which agents a [MAF workflow](/build/workflows) reaches is governed at authoring time — who can create a workflow and which agents they name — not by this allowlist. ## Role hierarchy **Enterprise feature.** This ranked role hierarchy is enforced by `nasiko-ee`'s auth layer. The open-source edition's auth has no roles — `can_deploy`, `can_manage_secrets`, and `can_manage_users` all default to allow-all for every authenticated user. Roles rank strictly: `admin` > `department_manager` > `team_lead` > `team_member` > `member`. An unrecognized role value ranks below all five rather than defaulting to trusted. Three capabilities are gated by a **minimum** role — anyone at or above qualifies: | Capability | Minimum role | Covers | | - | - | - | | Deploy and run agents | `member` | The baseline for every authenticated user | | Manage an agent's secrets | `team_lead` | Viewing which secrets are set, rotating, injecting | | Manage platform users | `admin` | Creating accounts, changing roles, moving people between teams | Role and superuser are independent. A superuser bypasses org-visibility scoping and unlocks platform-wide administrative routes regardless of `role`; promoting someone to `admin` does not grant superuser. A role is a capability tier, not a grant on any agent. Meeting a threshold means you may *attempt* that class of action — whether you can act on a *particular* agent is still decided by the ownership and grant model above. ## Org-scoped visibility **Enterprise feature.** Visibility scoping by team/department hierarchy is `nasiko-ee` only — the open-source edition's equivalent setting is unrestricted (every authenticated user can see every other user). The platform also scopes which *users* a caller can see. A non-superuser's visible set is bounded by their position in the hierarchy: a management role sees people within their own scope (their team, or their department and everything under it); a baseline user sees themselves and whatever their role otherwise entitles. Superusers see everyone. This is what keeps user-listing and user-search endpoints from leaking every organization's roster to every signed-in account on a shared platform. ## Related Roles, grants, and allowlists. Ownership, public flags, and grants day to day. The flow guard checked alongside the allowlist. A parallel permission model for tool access. # User and org management Source: https://docs.nasiko.com/governance/access-control/users-and-teams Create users, assign roles, and organize departments and teams. **Enterprise feature.** This entire page — user/department/team management, SSO group mappings, and Entra ID directory sync — is `nasiko-ee` only. The open-source edition has a much smaller surface: basic account creation/deactivation via `/api/users` (superuser-gated), with no departments, teams, roles, or directory sync. Administrators manage users, departments, and teams through the admin CLI (`nasiko-ee`) or the equivalent API routes. ## Creating a user ```bash theme={null} nasiko-ee users create --username alice --email alice@example.com --role team_member ``` No password is set. The platform returns a one-time key pair: ```json theme={null} { "id": "5b1e...", "username": "alice", "access_key": "ak_...", "access_secret": "as_...", "message": "Store access_secret securely — it won't be shown again." } ``` `access_secret` is shown once and is the user's initial login password. There is no recovery endpoint — if lost, an admin resets the account. `--role` defaults to `member`. Place the user at creation time: ```bash theme={null} # into a team (also sets their department) nasiko-ee users create --username bob --email bob@example.com --team platform-eng # into a department, no team nasiko-ee users create --username carol --email carol@example.com --dept engineering ``` Add `--superuser` for superuser privileges (separate from `--role` — see below). Move or remove users later: ```bash theme={null} nasiko-ee users place bob --team platform-eng nasiko-ee users place bob --dept engineering nasiko-ee users place bob --clear nasiko-ee users ls nasiko-ee users deactivate alice # keeps the record, blocks login nasiko-ee users delete alice ``` ## Role vs. superuser | | What it does | | - | - | | **`role`** | Places the user on the five-tier hierarchy, driving capability thresholds ([overview](/governance/access-control/overview)) | | **Superuser** | Bypasses org-visibility scoping; required for creating users, creating or deleting departments and teams, OIDC group mappings, and directory sync | They're checked separately everywhere. `role: admin` does not grant superuser. Set it with `--superuser` at creation, or on the Users page afterward. ## Departments and teams A department contains teams; a team belongs to exactly one department. Creating, updating, or deleting either requires superuser. ```bash theme={null} # Departments nasiko-ee department create --name Engineering --manager nasiko-ee department ls nasiko-ee department get nasiko-ee department update --manager nasiko-ee department members nasiko-ee department agents nasiko-ee department delete # Teams nasiko-ee team create --name Platform --dept --lead nasiko-ee team ls --dept nasiko-ee team get nasiko-ee team update --lead nasiko-ee team members nasiko-ee team agents nasiko-ee team delete ``` Assigning a `--manager` or `--lead` does not change that user's `role`. Bump it separately if they need the matching permission tier. ## Single sign-on and automatic placement Nasiko supports OIDC SSO with any standards-compliant provider. See [SSO setup](/governance/sso-and-scim). **Group-to-role mapping** works with any OIDC provider that emits a group claim. Map an external group to an internal role, team, and/or department; it applies on sign-in. A user matching several mappings gets the highest-ranked role's mapping applied in full — rows are not merged field by field. A user matching none keeps their current role, so manual promotions are never reset. **Directory sync** pre-provisions your whole hierarchy before anyone logs in. It requires Microsoft Graph and is Entra ID only. ```bash theme={null} nasiko-ee admin directory-sync ``` It infers the hierarchy from each user's department attribute and manager chain: the person with no in-department manager becomes department head, their reports who manage others become team leads, everyone else joins the nearest lead's team. Users with no department attribute become plain members. Sync is insert-only and safe to re-run — it never touches a user it already created, and never reverts a manual placement. If your directory changes significantly, reassign affected users by hand. A user who signs in via SSO with no mapping or sync record lands as a plain `member` with no team or department. An admin promotes and places them afterward. ## Next * [Access control overview](/governance/access-control/overview) — roles and permission tiers * [User → agent access](/governance/access-control/agent-access) — granting access to specific agents * [Secret management](/governance/secrets/overview) # Human-in-the-loop approvals Source: https://docs.nasiko.com/governance/approvals Pause an agent for a human decision — tool approval, extra input, or auth — then resume. Some agent steps should not run unattended: calling a destructive tool, asking the user a question, or starting an OAuth flow. Nasiko pauses the run, records a HITL request, and resumes when a human resolves it. Approvals surface in: * `nasiko chat` (orchestrator and `nasiko chat -a`) * `nasiko maf workflow run … --wait` * MCP tool calls whose permission is **ask** * The HTTP API below There is no separate Approvals screen in the open-source dashboard. Pending requests are prompted inline in the CLI. Superusers can requeue a stuck resume via the API. ## Kinds of pause | Kind | What the human does | | - | - | | `tool_approval` | Approve once, approve for the rest of the session, or reject. Optional audit note. | | `input_required` | Answer a free-text question, or pick from a structured single- or multi-select list. | | `auth_required` | Start an OAuth or credential flow, then confirm or deny. | MCP connectors with per-agent tool stance **ask** raise `tool_approval`. See [Tool permissions](/governance/tool-permissions). ## Resolve from the CLI When `nasiko chat` or `maf run --wait` hits a pause, the CLI prints the request and waits for a decision. Ctrl+C cancels the prompt without killing the session; the HITL row stays `pending` until you resolve or cancel it. `NO_COLOR` is honored. ## API All routes except requeue require the user who owns the pause (or an admin). Requeue is superuser only. | Method | Path | Purpose | | - | - | - | | `GET` | `/api/hitl/pending` | List pending requests | | `GET` | `/api/hitl/{id}` | One request | | `POST` | `/api/hitl/{id}/resolve` | Submit a decision | | `POST` | `/api/hitl/{id}/cancel` | Cancel | | `GET` | `/api/hitl/{id}/stream` | Resume SSE stream | | `POST` | `/api/hitl/{id}/requeue` | Superuser: requeue a stuck resume | ### Resolve body Fields used depend on the kind. Unused fields are ignored. ```json theme={null} { "answer": "Summary", "custom_answer": null, "auth_action": "confirm", "decision": "approve", "scope": "session", "note": "optional audit note" } ``` | Kind | Fields | | - | - | | `input_required` | `answer` — string, or string array for multi-select. `custom_answer` is extra "something else" text for multi-select. | | `tool_approval` | `decision`: `approve` or `reject`. `scope`: `once` or `session`. `note` is stored, not interpreted. | | `auth_required` | `auth_action`: `start` then `confirm`, or deny. | A 403 from the LLM router or MCP gateway is not a HITL pause — it is [attribution](/tokenops/attribution) or a permission denial. # Routing and flow limits Source: https://docs.nasiko.com/governance/flow-limits How the routing pipeline degrades, where it runs, and the flow guard that bounds every agent-to-agent call. The [routing engine guide](/build/routing-engine) covers what routing does. This page covers how each stage behaves under failure, where the pipeline runs, and the flow guard. ## How each stage degrades Agent selection runs in three stages, and **every stage fails forward**. The worst case is a slightly worse agent pick, never a dropped query. ```mermaid theme={null} flowchart LR Q(["Query + accessible agents"]) --> S1{"Stage 1: Shortlist
skip if fleet < threshold"} S1 --> S2{"Stage 2: Rerank
skip if no history or embed fails"} S2 --> S3{"Stage 3: Select
fallback to top candidate on LLM failure"} S3 --> R(["Chosen agent + fallback flag"]) ``` **Stage 1 — shortlist.** Fetches every accessible agent and the session's recent history in parallel, then narrows candidates by semantic similarity between the query and each agent's description. Agent embeddings are cached per agent. Skipped entirely — no embedding calls — when the accessible fleet is smaller than `ROUTER_SHORTLIST_THRESHOLD` (default `15`); below that, every agent goes straight to Stage 2. `ROUTER_SHORTLIST_SIZE` (default `10`) caps surviving candidates. **Stage 2 — rerank.** Re-scores candidates against the conversation's running history plus the current query, so a follow-up is more likely to stay with the agent already handling the session. Two conditions return the shortlist unchanged: empty conversation history (no embedding call at all) and an embedding-provider failure, which is caught and logged rather than propagated. **Stage 3 — select.** An LLM (`ROUTER_MODEL`, default `gpt-4o`) makes the final call. If it errors, the engine picks the highest-ranked remaining candidate and marks the decision `fallback_used: true`. ## Routing decisions are logged off the response path Decisions are recorded asynchronously — the response is already back by the time the log write happens. Each captures the agents considered, the reasoning, whether a fallback was used, per-stage candidate counts and latencies, and the selection call's token cost. The LLM call that *picks* an agent is metered like any other, not folded into the chosen agent — so routing cost is its own line item in [TokenOps](/tokenops/attribution). Aggregated stats come from `GET /api/orchestrator/stats`. They're a periodic rollup, so they lag recent activity — for a specific query, open its session trace. ## Where the pipeline actually runs The three-stage pipeline is *not* what picks the agent on every chat message. `POST /api/orchestrator/a2a` dispatches on `agent_id` metadata: | Request | What runs | | - | - | | Explicit `agent_id` (not `"orchestrator"`) | Proxies straight to that container. No routing decision. | | No `agent_id`, or `"orchestrator"` | A multi-turn ReAct loop — every accessible agent is exposed as a callable tool, and an LLM decides across up to ten turns which agents to call and in what order. No shortlist or rerank; more than one agent per query is possible. | The three-stage pipeline is invoked in exactly one place today: **auto-assigning an agent to a [MAF workflow](/build/workflows) step** when the step doesn't name one. If it errors, workflow creation falls back to matching the step's task description against the caller's registered agents by name and description. Both mechanisms answer "which agent should handle this," but they're different code paths. A workflow step's auto-assignment doesn't run a ReAct loop; a chat message doesn't run Stage 1/2/3. ## The flow guard Every agent-to-agent call Nasiko makes is wrapped by a flow guard, regardless of how the destination was picked. It enforces five limits scoped to a single flow, identified by the W3C `traceparent` trace ID: | Limit | Enforced on | Rejection | | - | - | - | | Max call depth | Every hop, before and after incrementing | `MaxDepthExceeded` | | Cycle detection | Every hop, against the running call chain | `CycleDetected` | | Max fan-out | Every hop, against total invocations so far | `MaxFanOutExceeded` | | Token budget | After each hop reports usage | `TokenBudgetExhausted` | | Wall-clock timeout | Every hop, against the flow's start time | `FlowTimeout` | Checks run *before* an agent is invoked, so a rejected call is never paid for. Counters are re-checked after incrementing in case a concurrent hop crossed a limit in between. If the guard's backing store is unreachable, it **fails closed** — calls are rejected rather than allowed through unchecked. The threat model is unvetted, user-authored agent and MCP code: silently disabling cascade limits during an outage would open an unbounded recursion and fan-out window. | Env var | Default | Limit | | - | - | - | | `NASIKO_FLOW_MAX_DEPTH` | `5` | Max call depth | | `NASIKO_FLOW_MAX_FAN_OUT` | `20` | Total invocations per flow | | `NASIKO_FLOW_MAX_TOKENS` | `100000` | Token budget per flow | | `NASIKO_FLOW_TIMEOUT_SECS` | `120` | Wall-clock timeout | | `NASIKO_FLOW_STATE_TTL_SECS` | `300` | How long guard state is retained | Live flow status is broadcast to the dashboard's [Flows view](/dashboard/sessions-and-traces#flows) (`/api/flows/*`) independently of the guard's limit tracking. ## Related Tuning knobs, inspecting decisions, reusable LLM configs. Configure providers and per-level models. Multi-agent workflows and step assignment. Sessions, traces, and how a flow's hops tie together. # Setting up an organization Source: https://docs.nasiko.com/governance/organizations Create departments, teams, and users, and grant them agent access with the admin CLI. **Enterprise.** Org hierarchy is `nasiko-ee` only. The command list on this page is from the published Enterprise CLI surface; this checkout does not include the `ee/cli` source, so flag names were not re-verified against a binary in this pass. Nasiko models an organization as a hierarchy: **departments** contain **teams**, teams contain **users**, users hold a **role**. Separately, **agent grants** control which agents a user, team, or department can reach. **Enterprise feature.** Org hierarchy — departments, teams, roles beyond a single flat account list — is provisioned and managed entirely through `nasiko-ee`. The open-source edition has no departments, teams, or roles; every user is simply an authenticated account. Nothing on this page applies to a `nasiko`-only (OSS) install. This page uses the admin CLI (`nasiko-ee`), not the developer CLI (`nasiko`). See [Install and connect](/install) and [access control](/governance/access-control/overview). ## Roles | Role | Scope | | - | - | | `admin` | Users, teams, and departments across the organization | | `department_manager` | Teams and members in their department | | `team_lead` | Secrets and membership in their team | | `team_member` | Contributor on a team | | `member` | Baseline user — can deploy and operate agents | Deploying requires `member`. Managing secrets requires `team_lead`. Managing users requires `admin`. Managing multiple Nasiko clusters? Most commands accept `--deployment `, or set a default with `nasiko-ee use `. ```bash theme={null} nasiko-ee department create --name Engineering --description "Product engineering" ``` Prints the department ID. Assign a manager with `nasiko-ee department update --manager `. ```bash theme={null} nasiko-ee team create --name Backend --dept --description "Backend services team" ``` Add `--lead ` now or later with `nasiko-ee team update --lead `. ```bash theme={null} nasiko-ee users create \ --username alice \ --email alice@example.com \ --role team_lead \ --team ``` `--role` defaults to `member`. `--team` also sets the user's department; use `--dept` to place someone in a department without a team. There's no `--password` flag. The server prints a one-time access key and secret; the secret is the initial login password. Capture it — it isn't retrievable. Move users later: `nasiko-ee users place --team ` (or `--dept`, or `--clear`). Role and team membership grant no agents by default. Grant explicitly: ```bash theme={null} nasiko-ee access grant --user alice nasiko-ee access grant --team nasiko-ee access grant --dept nasiko-ee access grant --public ``` `--user`, `--team`, and `--dept` accept multiple values and combine in one call. Revoke with `nasiko-ee access revoke`. To set an agent's *owning* team or department (not a grant): ```bash theme={null} nasiko-ee access org --team --dept ``` ## Inspecting ```bash theme={null} nasiko-ee department ls # list departments nasiko-ee department members # who's in a department nasiko-ee team ls --dept # teams in a department nasiko-ee team members # who's on a team nasiko-ee access ls # resolved users + grants nasiko-ee access visibility # public? every raw grant row nasiko-ee access my-agents # agents you can access ``` ## UI and API The same operations are available in the admin UI and the `/api` org routes. See [user management](/governance/access-control/users-and-teams) and [user-to-agent access](/governance/access-control/agent-access). Importing users from an identity provider? See [Entra ID and SSO](/governance/sso-and-scim). # Managing your agent's secrets Source: https://docs.nasiko.com/governance/secrets/agent-secrets Set, inspect, and rotate encrypted agent secrets via the CLI or API. Secrets are scoped to an agent or shared vault-wide, stored AES-256-GCM encrypted, and decrypted into the container environment at deploy time. See [secret management overview](/governance/secrets/overview) for the encryption model. This is separate from your project's local `.env` (used only by `nasiko run .`) and from the Nasiko server's own environment (used by the routing engine and orchestrator chat) — the same key commonly needs to be set in all three places for different parts of the platform to work. See the [quickstart prerequisites](/quickstart#prerequisites) for the full breakdown. ## CLI ```bash theme={null} # Scoped to one agent nasiko secrets set STRIPE_API_KEY sk_live_xxxxx --agent my-billing-agent # Vault-wide — available to any agent you deploy nasiko secrets set OPENAI_API_KEY sk-xxxxx # List names (values are never echoed back) nasiko secrets ls --agent my-billing-agent # Read metadata nasiko secrets get STRIPE_API_KEY --agent my-billing-agent # Remove nasiko secrets rm STRIPE_API_KEY --agent my-billing-agent ``` Without `--agent`, a secret lives at the vault level and any agent you deploy can reference it. ## API The same operations are split across two route families, not one: | Scope | Routes | | - | - | | Vault-wide | `GET`/`POST /api/secrets`, `GET`/`PUT`/`DELETE /api/secrets/{name}` | | Per-agent | `GET`/`POST /api/agents/{agent_id}/secrets`, `POST /api/agents/{agent_id}/secrets/import`, `DELETE /api/agents/{agent_id}/secrets/{name}` | Both are authenticated the same way. ## Who can manage secrets Setting, reading, or removing secrets requires the `can_manage_secrets` permission. See [access control](/governance/access-control/overview). ## How secrets reach a running agent Secrets aren't mounted as files or fetched at runtime. They're decrypted server-side and injected as environment variables at deploy time, alongside the observability configuration. To the agent, a secret is just an environment variable that's there at process start. Changing a secret's value doesn't affect an already-running container. Run `nasiko restart` to re-read the current secret set and re-inject it. ## Refresh on scale-up When an agent scales up, Nasiko refreshes secrets immediately before new replicas start, so every replica runs current values. Scaling up is itself a refresh point — no manual restart needed after rotating and then scaling. ## Related * [Secret management overview](/governance/secrets/overview) — encryption at rest * [Access control overview](/governance/access-control/overview) — role requirements * [Agent hosting](/self-hosting/agent-hosting) — the deploy and lifecycle flow # Secret management overview Source: https://docs.nasiko.com/governance/secrets/overview How Nasiko protects agent secrets and platform identity credentials. **Enterprise feature.** OSS ships the OIDC config/storage plumbing referenced elsewhere on this page, but the actual SSO login redirect/callback route is `nasiko-ee` only — see [SSO setup](/governance/sso-and-scim). Nasiko handles two categories of sensitive material, both encrypted at rest with **AES-256-GCM**: | Category | Examples | | - | - | | **Per-agent secrets** | API keys, database credentials, third-party tokens attached to your agents | | **Platform identity secrets** | SSO client secrets, session signing keys | ## Per-agent secrets Secrets are encrypted before they're written to the database and decrypted only when injected into a container's environment at deploy time. Plaintext exists in memory for that deploy call and then inside the running container — nowhere else. * Stored ciphertext-only. A database dump never exposes raw values. * Decryption happens once, server-side, immediately before the container starts. * Rotating a secret and restarting the agent re-injects the new value. Nothing is cached outside the container's environment. `SECRETS_ENCRYPTION_KEY` is a 32-byte base64 value validated at startup — the server refuses to start if it's missing or malformed. Generate it from a cryptographically secure source, store it in your secrets manager, and rotate it deliberately: rotation invalidates every secret encrypted under the old key, so all agent secrets must be re-entered afterward. See [managing agent secrets](/governance/secrets/agent-secrets) for the CLI and API workflow. ## Platform identity secrets Nasiko supports SSO through any OIDC-compliant identity provider (Entra ID, Okta, Auth0, Keycloak, and others). The client secret is encrypted at rest under the same scheme, whether configured via environment variables at deploy time or the admin settings API at runtime. See [SSO setup](/governance/sso-and-scim), including how group claims can place users into roles, teams, and departments automatically. ## Where this fits * [Access control overview](/governance/access-control/overview) — who can read and rotate secrets * [Agent hosting](/self-hosting/agent-hosting) — how secrets flow into a running container # Setting up SSO login Source: https://docs.nasiko.com/governance/sso-and-scim Configure single sign-on with your identity provider using OIDC. **Enterprise feature.** The open-source edition ships the underlying `oidc` client library and the settings storage referenced below (`GET`/`PUT /api/settings`), but no OSS route actually performs the `/api/auth/oidc/login`/`callback` redirect — that login flow, plus the group-mapping and directory-sync sections further down this page, are `nasiko-ee` only. Nasiko's login flow is built against generic OIDC, so Entra ID, Okta, Auth0, Keycloak, and Google all work the same way. This guide uses **Microsoft Entra ID** as the worked example. ## Two ways to configure it | Method | Takes effect | Notes | | - | - | - | | **Environment variables** | On restart | Set at deploy time | | **Admin settings API** (`PUT /api/settings`, superuser only) | Next login | Stored in the database, client secret encrypted at rest. Takes precedence over environment variables. | ```bash theme={null} curl -X PUT https:///api/settings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "oidc_issuer_url": "https://login.microsoftonline.com//v2.0", "oidc_client_id": "", "oidc_client_secret": "", "oidc_redirect_uri": "https:///api/auth/oidc/callback" }' ``` `GET /api/settings` never returns the raw secret, only whether one is configured. Omitting `oidc_client_secret` on a later update leaves the stored value untouched; sending an empty string clears it. The dashboard writes to the same store, under **Settings → Single Sign-On (Microsoft Entra)** — the four required fields, with a **Not configured** badge until all four are saved. ## Setup In Entra ID: [Microsoft Entra admin center](https://entra.microsoft.com) → **Identity → Applications → App registrations → New registration**. * **Name** — anything recognizable, e.g. `Nasiko Platform` * **Supported account types** — single tenant, unless you need multi-tenant * **Redirect URI** — platform type **Web**, value `https:///api/auth/oidc/callback`. Must match `OIDC_REDIRECT_URI` exactly: scheme, host, path, no trailing slash. From **Overview**: the **Application (client) ID** and **Directory (tenant) ID**. From **Certificates & secrets → New client secret**: copy the value immediately — it's shown once. Entra ID issuer URL: ``` https://login.microsoftonline.com//v2.0 ``` Add delegated `openid`, `profile`, and `email` under **Microsoft Graph** — usually pre-granted. For [directory sync](#directory-sync), also add `User.Read.All` as an **Application** permission and grant admin consent. | Variable | Value | Required | | - | - | - | | `OIDC_ISSUER_URL` | Your provider's issuer URL | Yes | | `OIDC_CLIENT_ID` | Application/client ID | Yes | | `OIDC_CLIENT_SECRET` | Client secret value | Yes | | `OIDC_REDIRECT_URI` | `https:///api/auth/oidc/callback` | Yes | | `OIDC_SCOPES` | Defaults to `openid profile email` | No | | `OIDC_PROVIDER_LABEL` | Defaults to `microsoft_entra` | No | SSO stays disabled until all four required variables are set. `GET /api/auth/oidc/status` returns `{"configured": true|false}` so your login screen can show or hide the button. Open `https:///api/auth/oidc/login`. You should reach your provider's login screen, then be redirected back to `/api/auth/oidc/callback` with a session established. ## Routes | Route | Purpose | | - | - | | `GET /api/auth/oidc/login` | Redirects to your provider's consent screen | | `GET /api/auth/oidc/callback` | Exchanges the code and logs the user in | | `GET /api/auth/oidc/status` | `{"configured": bool}` — safe for the frontend | ## First login A new SSO sign-in creates a local Nasiko user with the base role and no team or department. An admin promotes them from the **Users** page or the users API. See [access control](/governance/access-control/overview). To skip that step, map groups to roles or pre-provision your organization — both below. ## Automatic role assignment via security groups Entra ID's `roles` claim is a flat list with no concept of team or department, so **security groups** are the right primitive. Create one group per team or department, then map each to an internal role, team, and department. Group claims aren't on by default. In your app registration: **Token configuration → Add groups claim → Security groups**, leaving the format as group **Object IDs**. Then create a mapping (superuser only): ```bash theme={null} curl -X POST https:///api/oidc/group-mappings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "external_group_id": "", "role": "team_lead", "team_id": "", "department_id": "", "description": "Platform team lead group" }' ``` At least one of `role`, `team_id`, or `department_id` is required. If a user matches several mappings, the highest-ranked role's mapping wins outright — its fields apply together, not merged field by field. A user matching nothing is left untouched, so a manually promoted admin is never reset. | Route | Purpose | | - | - | | `GET /api/oidc/group-mappings` | List mappings | | `POST /api/oidc/group-mappings` | Create | | `GET /api/oidc/group-mappings/{id}` | Get one | | `PUT /api/oidc/group-mappings/{id}` | Update | | `DELETE /api/oidc/group-mappings/{id}` | Delete | All are superuser only. A group mapping can grant `admin` through group membership alone, so even read access to these routes is superuser-only — stricter than most org management endpoints. **Known limitation:** a user belonging to more groups than your provider's token overage threshold (typically 150 for Entra ID) has the `groups` claim omitted entirely and is left unmapped. ## Directory sync Rather than waiting for each user to log in and land as a bare member, an admin can trigger a one-time sync that fetches your tenant's directory, infers a department/team hierarchy, and creates matching users up front. First-time SSO sign-ins are matched to their pre-created account by identity-provider object ID, with role, team, and department already correct. Sync reuses the same app registration via Microsoft Graph's client-credentials flow, so the only extra setup is `User.Read.All` plus admin consent. Entra ID only, since it relies on Microsoft Graph. **Hierarchy inference** uses two directory attributes — `department` (free text) and `manager` — grouped per distinct department value: * No manager inside their department group → **department head** * A direct report of the head who has their own reports → **team lead** * Everyone below a lead, at any depth → that lead's team * Reports straight to the head with no reports of their own → department member, no team * No `department` attribute → plain member Trigger it (superuser only): ```bash theme={null} curl -X POST https:///api/org/directory-sync \ -H "Authorization: Bearer " ``` Or: ```bash theme={null} nasiko-ee admin directory-sync ``` The response reports `departments_created`, `teams_created`, `users_created`, `users_skipped_existing`, and `users_without_department`, plus any per-row errors, which don't abort the run. Sync is insert-only and safe to re-run. An identity seen from a prior sync or a prior login is never touched again. It won't revert a manual change, and it won't pick up later structural changes in your directory for someone already synced — reassign those users from **Users** or **Team Access**. ## Related * [Secret management overview](/governance/secrets/overview) — how the client secret is protected * [Access control overview](/governance/access-control/overview) — the role hierarchy SSO users resolve into # User → agent MCP access Source: https://docs.nasiko.com/governance/tool-permissions Which external tools and connectors an agent is allowed to call. Agents reach external tools through the [MCP gateway](/build/mcp/overview). This page covers per-agent connector and tool permissions — what an agent may *do* once running, independent of who can chat with it. ## Two layers, checked in order 1. **Reachability** — can the calling user reach the connector at all? They can if they own it, it's shared with them (by username — team/department sharing is `nasiko-ee` only, see below), or it's a platform-managed integration they've connected their own account to. A connector that fails this check is invisible — it doesn't appear in the tool list. 2. **Per-agent permission** — is the connector enabled for *this* agent, and is the specific tool allowed? Layer 2 is only consulted if Layer 1 passed. Reachability is re-evaluated on every call, never cached past a permission change. ## No configuration means fully allowed Per-agent permission is **default-allow**. Sharing a connector with someone makes it usable on every agent they run, immediately. A permission row exists only to *restrict*: disable a connector for one agent, or apply a rule to its tools. | Stance | Effect | | - | - | | `allow` | Callable normally (the default) | | `ask` | Blocked and reported as requiring approval | | `block` | Rejected outright | Rules are glob patterns against tool names: ```json theme={null} { "enabled": true, "tool_rules": [ { "pattern": "SEND_*", "stance": "block" }, { "pattern": "READ_*", "stance": "allow" }, { "pattern": "DELETE_*", "stance": "ask" } ] } ``` The connector stays enabled, but every `SEND_` tool is blocked, reads are allowed, and deletes need approval. ## Configuring it | Action | Route | | - | - | | View an agent's connector access | `GET /api/mcp/agents/{agent_id}/connectors` | | Set one connector's rules for one agent | `PUT /api/mcp/agents/{agent_id}/connectors/{connector_id}` | | Inspect a connector's tools and effective stances | `GET /api/mcp/agents/{agent_id}/connectors/{connector_id}/tools` | | View or bulk-update every tool rule | `GET` / `PUT /api/mcp/agents/{agent_id}/tools` | | Reset to default-allow | `DELETE /api/mcp/agents/{agent_id}/permissions` | Example body for the `PUT`: ```json theme={null} { "enabled": true, "tool_rules": [ { "pattern": "SEND_*", "stance": "block" } ] } ``` Configuring requires the ability to manage the agent (owner or superuser), *or* reachability to a connector already attached to it. The narrower path lets someone with a shared connector adjust it without gaining other control over the agent. ## Sharing a connector with a team or department **Enterprise feature.** Team/department connector sharing (this whole section) is `nasiko-ee` only. The open-source edition's connector sharing (see [MCP connector-sharing routes](/build/mcp/overview)) covers public, per-user, and per-agent shares — no teams or departments. Owners share by username through the [MCP connector-sharing routes](/build/mcp/overview). Team and department sharing is an admin operation: ```bash theme={null} nasiko-ee access mcp share --team platform-eng --dept engineering nasiko-ee access mcp unshare --team platform-eng nasiko-ee access mcp consumers # agents, users, teams using it ``` Revoking a share also deletes the grantee's stored credential for that connector. A later re-grant starts from a clean reconnect. ## Related * [MCP overview](/build/mcp/overview) — connectors, connections, request flow * [User → agent access](/governance/access-control/agent-access) * [Access control overview](/governance/access-control/overview) # Introduction Source: https://docs.nasiko.com/index The OpenRuntime for agents, coding harnesses, frameworks and tools. **Use any harness. Reach any model. Measure every token. Change nothing.** Find the coding agents already running. See what each one costs, by harness, model and team. Reach any model from any harness. One command on a laptop, the same command across an estate — and nothing changes about how anyone works. An OpenRuntime belongs to no vendor in it. Use any harness, any model, any framework, and change your mind about all three. Your cost history, policy and memory stay with you rather than with whichever tool you happened to pick. ## Two properties Your architecture. Nasiko discovers the harness you already run, and it keeps running unchanged. No wrapper. No SDK in your code path. Your day. One command to find your agents, one more to register each, and your workflow stays the same. Developers keep using the same harness commands and interfaces. ## Choose your path Discover Claude Code, Codex, OpenCode and the Cursor CLI on your machine, report their sessions, and see what each one costs. About ten minutes. Scaffold an agent in any framework, run it locally, deploy it to your Nasiko cluster, and chat with it. ## What Nasiko does Spend across every harness and framework in one schema — by model, agent, session, developer and team. Control what each agent can reach, enforced where the call happens: access grants, tool permissions, approvals, flow limits and secrets. Build, deploy and compose agents from any framework, and route each model call to the provider you choose. ## How a call flows Nasiko sits in the call path. That's what lets it attribute cost across vendors and enforce policy without owning the machine. ```mermaid theme={null} flowchart LR Harness(["Coding harness or deployed agent"]) -->|"model call"| Nasiko["Nasiko server"] Nasiko -->|"routed to the model you chose"| Provider["Model provider"] Nasiko -->|"proxied A2A call"| Agent["Another agent"] Nasiko -->|"permission-filtered tools"| Tools["MCP tools"] Nasiko -.->|"tokens, cost, traces"| TokenOps["TokenOps"] ``` A few terms you'll see throughout the docs: * **Coding agent** or **harness**: a developer tool such as Claude Code, Codex, OpenCode or the Cursor CLI. Nasiko discovers it and can report its sessions and route its model calls. The harness itself is unchanged. * **Deployed agent**: an agent you build in any framework and run on your Nasiko cluster. It speaks the A2A protocol. * **Cluster**: a Nasiko server the CLI is connected to — local on your laptop, or shared across your organization. * **Session**: one conversation, whether it came from a harness, the dashboard, or the CLI. * **Flow**: everything one request caused — every agent hop, model call and tool call — traced and attributed together. ## Explore Discovery, session reporting and model routing. Route any harness or agent to any provider. One permission-filtered tool surface for every agent. Every screen, explained. Run the full stack with Docker Compose. Every command and flag. # Install and connect Source: https://docs.nasiko.com/install Install the Nasiko CLI, get a Nasiko cluster to connect to, and sign in. You need three things: the `nasiko` CLI, a Nasiko cluster to connect it to, and an account on that cluster. Coding-agent discovery (`nasiko agents discover`) works with just the CLI. Nasiko ships two command-line tools, both standalone Rust binaries: | CLI | Binary | For | | - | - | - | | Developer CLI | `nasiko` | Discovering and routing coding agents; building, deploying and chatting with agents | | Admin CLI | `nasiko-ee` | Provisioning clusters and managing organizations, teams and grants. Enterprise only | ## Install the CLI ```bash theme={null} git clone https://github.com/Nasiko-Labs/nasiko.git cd nasiko cargo install --path cli --force nasiko --help ``` This installs to `~/.cargo/bin`. Reinstall with `--force` to update. Every build reports version `0.1.0`, so `nasiko --version` can't tell you whether a newer command is present. The dashboard has the same steps with copy buttons: click **Set up CLI** to open a walkthrough covering install, connecting a cluster, creating your first agent, and deploying it. ## Get a cluster Get the URL and an account from your administrator. An administrator creates your account and gives you either a username and password, or an access key and secret. The secret is shown once, so save it. Runs the full stack — server, Postgres, Redis, S3 storage, and the trace and log backends — in Docker: ```bash theme={null} cp .env.example .env # set ADMIN_PASSWORD, OPENAI_API_KEY and AGENT_JWT_SECRET docker compose up -d ``` The dashboard is at [http://localhost:8080](http://localhost:8080). See [Deploy the stack](/self-hosting/deploy). Starts the infrastructure in Docker and runs the Nasiko server as a local process: ```bash theme={null} nasiko up ``` `nasiko up` shows the settings it will use from `~/.nasiko/.env` and lets you edit them before starting. Answer the `OPENAI_API_KEY` prompt — the orchestrator and agent-card generation need it. When the server is healthy, the CLI connects to it as cluster `local`. Stop everything with `nasiko down`. See the troubleshooting section below for the settings `nasiko up` doesn't set for you. ## Connect and sign in ```bash theme={null} nasiko connect https://nasiko.example.com --name work ``` The CLI checks the cluster's health endpoint, saves it, and makes it the active cluster. `nasiko up` does this step for you. ```bash theme={null} nasiko clusters # list registered clusters nasiko use work # switch the active one ``` Every command runs against the active cluster. ```bash theme={null} nasiko auth login # username and password, or access key and secret nasiko auth whoami ``` Your token is stored in `~/.nasiko/config.json`. Check it with `nasiko auth status`. Sign out with `nasiko auth logout`. Signing in (and `connect` or `use` while signed in) also installs session reporting for any detected coding agent that doesn't have it yet. See [Discover and register](/coding-agents/discover). The dashboard and the CLI use the same account. Sign in to the dashboard at your cluster URL. ## Troubleshooting `nasiko up` The server needs `ADMIN_PASSWORD` and `JWT_SECRET` at startup, and `nasiko up` doesn't set them. Export them in the shell you run it from: ```bash theme={null} export ADMIN_PASSWORD=choose-a-password export JWT_SECRET=$(openssl rand -base64 48) nasiko up ``` Then sign in as `admin` with the password you chose. Model routing needs `AGENT_JWT_SECRET` on the server, and `nasiko up` doesn't set it. Without it, the server refuses to issue routing credentials. Deployed agents also need `LLM_GATEWAY_BASE_URL` to be routed through Nasiko. Export both before starting: ```bash theme={null} export AGENT_JWT_SECRET=$(openssl rand -base64 48) export LLM_GATEWAY_BASE_URL=http://localhost:8080 nasiko up ``` `nasiko up` doesn't set `TEMPO_URL`, `LOKI_URL` or `CODING_AGENT_OTLP_ENDPOINT`, so the server starts with observability off even though Tempo and Loki are running. Export them before starting: ```bash theme={null} export TEMPO_URL=http://localhost:3200 export LOKI_URL=http://localhost:3100 export CODING_AGENT_OTLP_ENDPOINT=http://localhost:4318 nasiko up ``` Or use Docker Compose, which sets all of these. `nasiko up` pulls `nasiko/cp:latest` from Docker Hub (or `/cp:latest` if you set `DOCKERHUB_USER`) and extracts the server binary to `~/.nasiko/bin/nasiko-cp`. It only pulls when that file doesn't exist. If the pull fails, build the server from source and put it there: ```bash theme={null} cargo build --release -p nasiko-server mkdir -p ~/.nasiko/bin cp target/release/nasiko-server ~/.nasiko/bin/nasiko-cp ``` The infrastructure containers run detached, but the server is a child process of the shell you ran `nasiko up` in. Closing that terminal stops it. Keep the terminal open, or use Docker Compose, which runs the server as a container. ## Admin CLI (`nasiko-ee`) **Enterprise.** Everything in this section applies to the Enterprise edition only. ```bash theme={null} curl -fsSL https://get.nasiko.dev/ee | bash ``` Anonymous download, no login. Installs to `~/.local/bin`, never uses `sudo`, and never edits your shell rc files — it prints the `PATH` line to add. | Variable | Default | Purpose | | - | - | - | | `NASIKO_EE_VERSION` | `latest` | Pin a version, for example `v0.1.0` | | `NASIKO_INSTALL_DIR` | `~/.local/bin` | Install destination | | `NASIKO_REGISTRY` | `registry.nasiko.dev` | Registry host, for mirrors or air-gapped installs | If piping `curl` into `bash` is disallowed at your organization, download and review the script first. Three variants, identical except for cluster provisioning: | Variant | Command | Binary | Provisioning commands | | - | - | - | - | | Default | `cargo install --path ee/cli --force` | `nasiko-ee` | Full | | Full, as `nasiko` | `just install-cli-enterprise` | `nasiko` | Full | | Lightweight | `just install-cli-enterprise-subset` | `nasiko` | Stubbed | The lightweight variant stubs `cluster create` and `init --agent-runtime kubernetes`; everything else behaves identically. Installing under the `nasiko` name **replaces** the developer CLI in `~/.cargo/bin`. Switch back with `cargo install --path cli --force`. Verify with `nasiko-ee --version`. ### Provision a cluster ```bash theme={null} nasiko-ee init --setup ``` This creates `~/.nasiko/.ee.env` with a template for cloud credentials (AWS, DigitalOcean, Azure or GCP), your Docker Hub org, and optional keys such as `OPENAI_API_KEY`. Fill it in, then: ```bash theme={null} nasiko-ee init \ --provider do \ --region blr1 \ --domain nasiko.example.com \ --admin-password ``` Next: [set up your organization](/governance/organizations). ### Registry access The Enterprise server image comes from a private registry. Save the read-only pull token Nasiko provides: ```bash theme={null} nasiko-ee activate ``` This writes `~/.nasiko/registry-values.json`, which the cluster install commands use to authenticate pulls. All `nasiko-ee` commands: [Enterprise CLI reference](/reference/cli/enterprise). # LLM configs Source: https://docs.nasiko.com/models/llm-configs Reusable named configs that say which provider, model, key and fallbacks an agent or harness is routed to. An LLM config is a named routing choice: provider, model, API key secret, optional fallbacks, and optional pin. Create it once, then attach it to any deployed agent you own or pass it to `nasiko connect` for a coding harness. Configs live in your library on the cluster. They are not stored in the harness. ## Create and list ```bash theme={null} nasiko llm-config create --name my-openai --provider openai --model gpt-4o \ --api-key-secret OPENAI_API_KEY --secret-value "$OPENAI_API_KEY" nasiko llm-config ls nasiko llm-config ls --json nasiko llm-config providers # valid provider and model values ``` | Flag | Purpose | | - | - | | `--name` | Library name | | `--provider` | Provider id from `providers` | | `--model` | Model id. If you omit it on a later update, the router falls through [tier mappings](/models/model-registry) then the cluster default | | `--fallback` | Repeatable fallback model | | `--temperature`, `--max-tokens` | Sampling | | `--api-key-secret` | Name of a vault secret holding the key | | `--secret-value` | Plaintext to store under that secret name if it does not exist yet | | `--pin` / `--pinned-model` | Pin routing so the smart router never re-selects | | `--default` | Mark this as your default config | ## Update, default, delete ```bash theme={null} nasiko llm-config update my-openai --model gpt-4o-mini nasiko llm-config update my-openai --clear-fallbacks --no-pin nasiko llm-config set-default my-openai nasiko llm-config delete my-openai # must be detached from all agents first nasiko llm-config delete my-openai --force ``` ## Attach to a deployed agent ```bash theme={null} nasiko llm-config attach my-agent my-openai nasiko llm-config attach my-agent my-openai --inbound-format anthropic nasiko llm-config get my-agent nasiko llm-config detach my-agent # falls back to your default, if any ``` `--inbound-format` is `openai`, `anthropic`, or `gemini`: the SDK format the agent's code speaks. Nasiko translates to the outbound provider. The dashboard **LLM router** screen creates the same configs with a model per reasoning level. CLI `--model` is the explicit model; reasoning levels are the screen's way to fill the same library. ## Attach to a coding harness ```bash theme={null} nasiko connect claude --config my-openai nasiko connect codex --config my-openai nasiko connect opencode --config my-openai ``` Omit `--config` to use your default. See [Route models](/coding-agents/routing). ## API | Method | Path | | - | - | | `GET`, `POST` | `/api/llm-configs` | | `GET`, `PATCH`, `DELETE` | `/api/llm-configs/{id}` | | `POST`, `DELETE` | `/api/llm-configs/{id}/default` | | `GET`, `PATCH`, `DELETE` | `/api/agents/{id}/llm-config` | | `POST` | `/api/agents/{id}/llm-token` | Every flag: [CLI reference](/reference/cli/models). # LLM router Source: https://docs.nasiko.com/models/llm-router Connect a model provider and assign a model to each reasoning level. The **LLM router** screen assigns a model to each of three **reasoning levels**. The router then picks the right-sized model per request, so a quick lookup doesn't run on your most expensive model. ## The three reasoning levels | Level | Use it for | | - | - | | **Advanced reasoning** | Coding, planning, complex analysis | | **Balanced** | Most everyday requests | | **Fast responses** | Simple requests where speed is preferred | Every config assigns a model per level. One is enough. ## The screen With no configs, the page shows **No configs yet!** and a **Setup new config** button. Each config appears as a card with its name, provider, the model per level, and a **Default** badge on the current default. Actions: **Make default** (on non-default configs) and **Delete**. **Available providers** below lists every usable provider with its model count and a **Requires API key** chip. Clicking one opens the config form with that provider preselected. ## Creating a config The provider choice unlocks the reasoning-level dropdowns — they stay disabled until Nasiko knows which models to offer. * **Use saved secret** (default) — pick from secrets already in your workspace. See [Secrets](/governance/secrets/overview). * **Add new secret** — name it (e.g. `OPENAI_API_KEY`) and paste the key. Stored encrypted, used only for that provider. Either way, the key is never displayed back to you. Each dropdown lists only that provider's models; switching provider repopulates all three. At least one is required — saving all three empty returns *"Choose a model for at least one reasoning level."* Tick **Make this the default routing config** if it should be the default. It's ticked automatically for your first config. Changing the default config changes which models subsequent requests use. It doesn't affect conversations that already ran, and it doesn't restart any agent. ## Model routing vs. agent selection Two things get called "routing." This screen controls only the first: | Decision | Where it's configured | | - | - | | Which **model** answers | This screen | | Which **agent** answers | [Orchestrator and routing engine](/build/routing-engine) | Agent selection decides *who* handles the request; your routing config decides *which model* that work runs on. To lock a specific agent to one model, pin an [LLM config](/models/llm-configs): `nasiko llm-config create ... --pin`. ## Related Create, attach, pin, and fall back. Built-in catalog and custom endpoints. What your model choices cost. Where provider keys are stored and rotated. # Model registry Source: https://docs.nasiko.com/models/model-registry The cluster-wide (provider, tier) → model table the smart router falls through when a config does not pin a model. The model registry maps a **provider** and a **strength tier** to a model id. When an [LLM config](/models/llm-configs) has no `model` (or is not pinned), the smart router uses this table, then the cluster default. Tiers: | Tier | Meaning | | - | - | | `1` | Strongest — matches **Advanced reasoning** on the LLM router screen | | `2` | Balanced | | `3` | Smallest / fastest | ## List and set ```bash theme={null} nasiko model-registry ls nasiko model-registry ls --json nasiko model-registry set --provider openai --tier 3 --model gpt-4o-mini ``` `set` is **superuser only**. `ls` is available to any authenticated user. ## API | Method | Path | Who | | - | - | - | | `GET` | `/api/model-registry` | Authenticated | | `PUT` | `/api/model-registry` | Superuser | Body for `PUT`: `provider`, `tier`, `model`. The dashboard LLM router screen writes reasoning-level models into your LLM config library. The model registry is the cluster-wide fallback underneath that library, not a second place to assign per-agent models. # Providers Source: https://docs.nasiko.com/models/providers Built-in model providers and custom OpenAI-compatible endpoints — LiteLLM, vLLM, Ollama, Azure, Bedrock, OpenRouter. Nasiko does not host models. It routes to the providers you already have. Built-in providers ship in the catalog. Custom providers are any OpenAI-compatible endpoint you register. ## Built-in catalog ```bash theme={null} nasiko llm-config providers ``` The **LLM router** screen lists the same catalog under **Available providers**, with a model count and a **Requires API key** chip. Click a provider to create a config with it preselected. Keys are stored as [secrets](/governance/secrets/overview); they are never displayed back. API: `GET /api/llm-router/providers`. ## Custom providers On the LLM router screen, **Register any OpenAI-compatible endpoint** adds a provider whose models then appear for tier routing. Supported kinds: | Kind | What to point it at | | - | - | | `openai` | LiteLLM, vLLM, Ollama, OpenRouter, or any internal OpenAI-compatible gateway | | `azure-openai` | Azure OpenAI | | `bedrock-converse` | AWS Bedrock Runtime (Converse API). Models are discovered from the account. API key starts with `ABSK`. | Superuser only for create, update, delete, test and sync. Any authenticated user can list providers and list a provider's models. | Method | Path | Purpose | | - | - | - | | `GET` | `/api/custom-providers` | List | | `POST` | `/api/custom-providers` | Register (superuser) | | `POST` | `/api/custom-providers/test` | Probe an endpoint (superuser) | | `GET` | `/api/custom-providers/{id}/models` | Models | | `PATCH`, `DELETE` | `/api/custom-providers/{id}` | Update / delete (superuser) | | `POST` | `/api/custom-providers/{id}/sync` | Refresh the model catalog (superuser) | Until a [price book](/tokenops/pricing) row exists for a custom model, TokenOps marks its cost **estimated**. ## How agents reach a provider Deployed agents never hold the real key. At deploy time Nasiko points the agent's SDK at the cluster (`LLM_GATEWAY_BASE_URL`) and injects a short-lived identity JWT as `OPENAI_API_KEY`. Coding harnesses use the same router through [model routing](/coding-agents/routing). The router endpoints: | Method | Path | Inbound protocol | | - | - | - | | `POST` | `/v1/chat/completions` | OpenAI Chat Completions | | `POST` | `/v1/responses` | OpenAI Responses (Codex) | | `POST` | `/v1/messages` | Anthropic Messages (Claude Code) | | `POST` | `/v1beta/models/{model_method}` | Gemini generate / stream | | `POST` | `/v1/embeddings` | Embeddings | | `GET` | `/v1/models` | List models | | `GET` | `/v1/health` | Liveness (no auth) | All except `/v1/health` require an agent JWT. Missing or unresolvable `traceparent` is a 403 — see [cost attribution](/tokenops/attribution). # Quickstart Source: https://docs.nasiko.com/quickstart Find the coding agents already running on your machine, see what each one costs, and route one to the model you choose. In about ten minutes you'll install the CLI, discover the coding agents on your machine, start reporting their sessions to a Nasiko cluster, and see their spend in TokenOps. Routing a harness to a different model is an optional last step. Nothing about how you use your harnesses changes. Claude Code, Codex, OpenCode and the Cursor CLI keep running exactly as they do today. Want to build and deploy your own agent instead? Follow [Quickstart: deploy an agent](/quickstart-deploy). ## Prerequisites * [Rust](https://rustup.rs) to install the CLI * [Docker](https://docs.docker.com/get-docker/) with Compose, to run a local Nasiko cluster — or the URL of your organization's cluster and an account on it * At least one supported coding agent installed: Claude Code, Codex, OpenCode or the Cursor CLI ## Find and measure your coding agents ```bash theme={null} git clone https://github.com/Nasiko-Labs/nasiko.git && cd nasiko cargo install --path cli --force ``` This installs `nasiko` into `~/.cargo/bin`. Use `--force` when you update: every build reports version `0.1.0`, so the version number alone won't tell you whether you have the latest commands. ```bash theme={null} nasiko agents discover ``` ```text theme={null} AGENT DETECTED CONNECTED VERSION CONFIG ---------------------------------------------------------------------------------------- claude yes no - ~/.claude codex yes no - ~/.codex opencode - no - ~/.config/opencode cursor yes no - ~/.cursor ``` Discovery reads your machine, prints one row per supported harness, and exits. It changes no settings, needs no account, and sends nothing off the machine. **Detected** means the harness binary is on your `PATH` or its config directory exists. **Connected** means session reporting is installed, and **Version** is the version of the Nasiko reporting hook. From the `nasiko` repo you cloned: ```bash theme={null} cp .env.example .env ``` Edit `.env` and set: * `ADMIN_PASSWORD`: the password for the `admin` account * `OPENAI_API_KEY`: a model provider key * `AGENT_JWT_SECRET`: uncomment it and set a random value (`openssl rand -base64 48`). Model routing in the last step needs it. ```bash theme={null} docker compose up -d nasiko connect http://localhost:8080 --name local ``` The first build compiles the server inside Docker and takes a few minutes. The dashboard is at [http://localhost:8080](http://localhost:8080). The Compose stack already sets up everything coding-agent cost reporting needs. See [Deploy the stack](/self-hosting/deploy) for details. ```bash theme={null} nasiko connect https://nasiko.example.com --name work ``` Get the URL and an account from your administrator. ```bash theme={null} nasiko auth login ``` When you sign in, Nasiko installs session reporting for every detected harness that doesn't already have it, and registers each harness with the cluster. Run `nasiko agents discover` again and **Connected** reads `yes`. To install reporting for one harness explicitly, or to leave prompt and response text out of what's reported: ```bash theme={null} nasiko agents install claude nasiko agents install codex --no-content ``` Restart OpenCode after reporting is installed so it loads the plugin. Codex asks you to trust the new Nasiko hooks the next time it starts. Use your harness the way you always do. Each completed turn is queued on your machine and delivered to the cluster in the background. If the cluster is unreachable, turns wait in the queue and the harness is never blocked. ```bash theme={null} nasiko sessions # every harness session, alongside your other chats nasiko history # the turns in one session ``` In the dashboard, **Sessions** lists the same conversations. **Overview** and **TokenOps** break down spend by agent — each harness is registered as its own agent — and by model. See [the TokenOps dashboard](/tokenops/dashboard). Harnesses arrive tied to a vendor: Claude Code to Anthropic, Codex to OpenAI. Routing gives the model choice back to you. Register a provider key once, then connect a harness to it: ```bash theme={null} nasiko llm-config create --name my-openai --provider openai --model gpt-4o \ --api-key-secret OPENAI_API_KEY --secret-value "$OPENAI_API_KEY" nasiko connect claude --config my-openai ``` Claude Code keeps speaking its own protocol, and Nasiko routes each call to the model in your config. Routing is available for Claude Code, Codex and OpenCode. Undo it at any time with `nasiko disconnect claude`. See [Route models](/coding-agents/routing) for what changes on your machine and how failures behave. ## Next steps What's captured, where it's queued, and how delivery is kept safe. One cost schema across every harness and vendor. Claude Code, Codex, OpenCode and Cursor specifics. Build and run your own agent on the same cluster. Stuck? See [Troubleshooting](/coding-agents/troubleshooting), ask in [Discord](https://discord.com/invite/HmnfkTfjFv), or email [support@nasiko.com](mailto:support@nasiko.com). # Quickstart: deploy an agent Source: https://docs.nasiko.com/quickstart-deploy Scaffold an agent, run it locally, deploy it to your Nasiko cluster, and see its sessions and spend. Scaffold an agent in any framework, run it on your laptop, deploy it to your Nasiko cluster, and chat with it. Every model call it makes is routed through Nasiko and attributed in TokenOps. You don't add an SDK or change the agent's code. Here to measure the coding agents you already use? Start with the [Quickstart](/quickstart) instead. ## Prerequisites * [Docker](https://docs.docker.com/get-docker/), to build and run agent images * [Rust](https://rustup.rs), to install the CLI * A Nasiko cluster with a model provider key configured — run one locally with [Deploy the stack](/self-hosting/deploy), or use your organization's * An account with deploy permission — see [access control](/governance/access-control/overview) ## Build and deploy ```bash theme={null} git clone https://github.com/Nasiko-Labs/nasiko.git && cd nasiko cargo install --path cli --force nasiko connect http://localhost:8080 --name local # or your organization's URL nasiko auth login ``` Your token is stored in `~/.nasiko/config.json`. More options, including `nasiko up`: [Install and connect](/install). ```bash theme={null} nasiko new openai my-first-agent cd my-first-agent ``` This writes an `AgentCard.json` (the agent's identity and skills), a `Dockerfile`, and starter source in `src/`. Run `nasiko new` with no arguments to pick a framework interactively. See [Scaffold an agent](/build/scaffold). ```bash theme={null} nasiko run . ``` This builds the image and runs it on `localhost:8000`. When a cluster is connected, the local container gets the same model settings the cluster injects at deploy time, so you don't need to copy an API key into the project. A project `.env` file, if present, is loaded too. In a second terminal: ```bash theme={null} nasiko chat http://localhost:8000 "Customer says their invoice is wrong" ``` More: [Run and chat locally](/build/run-locally). ```bash theme={null} nasiko deploy . ``` This builds the image, pushes it to your cluster's built-in registry, then registers and starts the agent. It prompts for a version (use `-y` to accept the suggested one) and writes `.nasiko/agent.json`, which binds this directory to the deployed agent. No local Docker? Run `nasiko upload .` and the cluster builds the image for you. See [Deploy an agent](/build/deploy). ```bash theme={null} nasiko chat -a my-first-agent "Customer says their invoice is wrong" nasiko ps # status of your running agents nasiko logs my-first-agent # if something looks wrong ``` Omit the agent name (`nasiko chat "..."`) to let the orchestrator pick from every agent you can access. Sign in at your cluster URL. Your agent is already: * listed under **Your agents**, with status and owner — see [Agents](/dashboard/agents) * recording every conversation in **Sessions** — see [Sessions and traces](/dashboard/sessions-and-traces) * reporting spend by model in **TokenOps** — see [the TokenOps dashboard](/tokenops/dashboard) ## How the agent reaches a model At deploy time, Nasiko points the agent's model SDK at the cluster's [LLM router](/models/llm-router) and gives it a short-lived identity credential instead of a real provider key. The router selects the provider and key server-side, so no agent and no log ever holds the real key. To route this agent to a different model or provider, attach an [LLM config](/models/llm-configs): ```bash theme={null} nasiko llm-config attach my-first-agent my-openai ``` To give the agent its own provider key or any other secret, store it encrypted and restart: ```bash theme={null} nasiko secrets set MY_API_KEY --agent my-first-agent nasiko restart my-first-agent ``` See [agent secrets](/governance/secrets/agent-secrets). ## Next steps Ship a new version, or roll back to an earlier one. Control who can use and manage this agent. Give the agent permission-filtered tools. Chain agents into a multi-agent workflow. Stuck? Ask in [Discord](https://discord.com/invite/HmnfkTfjFv) or email [support@nasiko.com](mailto:support@nasiko.com). # Chat commands Source: https://docs.nasiko.com/reference/cli/chat chat, sessions, create-session, history, delete-session. Guide: [Run and chat locally](/build/run-locally), [Approvals](/governance/approvals). | Command | Flags | Purpose | | - | - | - | | `nasiko chat [url] [message]` | `-a/--agent`, `--tui`, `--resume`, `--session-id` | A2A chat. Omit target → orchestrator. A lone argument with whitespace is the message | | `nasiko sessions` | `--endpoint`, `--cursor`, `--limit` | List (includes coding-agent sessions) | | `nasiko create-session` | `--agent` | Create. Omit `--agent` for orchestrator | | `nasiko history ` | | Messages | | `nasiko delete-session ` | `-y/--yes` | Delete | HITL pauses prompt inline in `chat` and in `maf workflow run --wait`. # Coding agents commands Source: https://docs.nasiko.com/reference/cli/coding-agents discover, install, uninstall, sync, connect, disconnect, status, claude. Guides: [Coding agents](/coding-agents/overview). ## Session reporting | Command | Flags | Purpose | | - | - | - | | `nasiko agents discover` | | Local detection table | | `nasiko agents install ` | `--no-content` | Register + install hooks. `agent`: `claude`, `codex`, `opencode`, `cursor` | | `nasiko agents uninstall ` | | Remove Nasiko hooks only | | `nasiko agents sync` | | Drain `~/.nasiko/integrations/queue/` | `--no-content` omits prompts, responses, titles, and tool I/O. Tokens, models, and tool metadata still report. ## Model routing | Command | Flags | Purpose | | - | - | - | | `nasiko connect ` | `--agent`, `--config` | Point the harness at Nasiko | | `nasiko disconnect ` | `--force` | Restore settings | | `nasiko status ` | | Binding | | `nasiko claude` | `--agent` (required), `--config`, `[args...]` | One-shot Claude Code with env injected. Args after `--` go to `claude` | Cursor has no `connect` / `disconnect` / `status` target. Credential helpers `__claude-token` and `__coding-agent-token` are hidden. # Create commands Source: https://docs.nasiko.com/reference/cli/create new, build, run, validate, card, skill. Guide: [Scaffold an agent](/build/scaffold). | Command | Flags | Purpose | | - | - | - | | `nasiko new [template] [name]` | | Scaffold. Omit template for interactive. Writes `AgentCard.json`, `Dockerfile`, `src/` | | `nasiko build [dir]` | `--tag`, `--platform`, `-v/--version`, `-y/--yes` | Build image. Default dir `.` | | `nasiko run [path]` | `--port` default `8000` | Build + run locally | | `nasiko validate [dir]` | | Check card, Dockerfile, `src/` | | `nasiko card [description]` | `--dir` default `.` | Generate or update `AgentCard.json` | ## Skills | Command | Flags | | - | - | | `nasiko skill add ` | `--dir` default `.` | | `nasiko skill remove ` | `--dir` | | `nasiko skill list` | `--dir` | | `nasiko skill search [query]` | `--framework` | | `nasiko skill info ` | | # Enterprise CLI Source: https://docs.nasiko.com/reference/cli/enterprise nasiko-ee: provision, organizations, access, registry publish. **Enterprise.** `nasiko-ee` is not in the open-source tree. Commands below are documented from the published Enterprise CLI surface and [organizations](/governance/organizations). Confirm against a built `nasiko-ee` binary before treating a flag as exact. Install: [Install and connect](/install#admin-cli-nasiko-ee). Most commands accept `--deployment `. Default: `nasiko-ee use `. ## Provision | Command | Purpose | | - | - | | `nasiko-ee init --setup` | Write `~/.nasiko/.ee.env` template | | `nasiko-ee init --provider --region --domain --admin-password` | Provision a cluster | | `nasiko-ee activate ` | Save registry pull token to `~/.nasiko/registry-values.json` | Lightweight install stubs `cluster create` and `init --agent-runtime kubernetes`. ## Organization ```bash theme={null} nasiko-ee department create|ls|get|update|members|agents|delete nasiko-ee team create|ls|get|update|members|agents|delete nasiko-ee users create|place|ls|deactivate|delete nasiko-ee admin directory-sync ``` `users create`: `--username --email [--role] [--team|--dept] [--superuser]`. No `--password` — the server prints a one-time access key and secret. ## Access ```bash theme={null} nasiko-ee access grant [--user|--team|--dept|--public] nasiko-ee access revoke [...] nasiko-ee access org --team --dept nasiko-ee access ls|visibility|list-users|dept-grants|team-grants nasiko-ee access accessible-agents|my-agents|user-agents nasiko-ee access rename|owner nasiko-ee access mcp share|unshare|consumers ``` ## Registry ```bash theme={null} nasiko-ee registry publish --owner [--type] nasiko-ee registry yank nasiko-ee registry init --host --domain --admin-password nasiko-ee registry status|upgrade|logs ``` # Integration commands Source: https://docs.nasiko.com/reference/cli/integrations agents catalog, github, registry. Coding-agent `discover` / `install` live on [Coding agents](/reference/cli/coding-agents). ## agents (catalog) | Command | Flags | | - | - | | `ls` / `list` | Deployed + registered coding agents | | `get` | `--agent-id` / `--name`, `-f/--format` default `details` | | `deploy ` | `-n/--name` | | `search [query]` | `-t --artifact-type`, `-f --framework`, `--tags`, `-o --owner`, `-l` default `50` | | `info ` | `-o` default `nasiko`, `-v` | | `frameworks` | | | `list-uploaded` | | | `chat` | `-u` default `http://localhost:8000`, `[message]`, `-s/--session-id` | ## github | Command | Flags | | - | - | | `status` / `repos` / `connect` / `disconnect` | | | `clone [owner/repo]` | `--branch` default `main` | ## registry Artifact registry client (separate host from the cluster). | Command | Flags | | - | - | | `connect ` / `disconnect` / `status` | | | `search` alias `discover` | `[query]`, `-t/--type`, `-f/--framework`, `--top` default `10`, `--min-score`, `-j` | | `list` | `-t`, `-j` | # MCP commands Source: https://docs.nasiko.com/reference/cli/mcp catalog, connect, connectors, credentials, OAuth, agent-tools. Guide: [MCP gateway](/build/mcp/overview). ## Connect | Command | Flags | | - | - | | `nasiko mcp catalog` | `-j/--json` | | `nasiko mcp connect` | `--connector-id` \| `--toolkit` \| `--url`, `--value`, `--redirect-url` | | `nasiko mcp connections` | `-j` | | `nasiko mcp disconnect ` | | | `nasiko mcp share-targets ` | `-j` | ## Toolkits (Composio, admin) `nasiko mcp toolkit list|register|update|delete` `register` takes `toolkit`, `--client-id`, `--client-secret`, `--scope` (repeat), `--display-name`, `--logo_url`. ## Connectors | Command | Notes | | - | - | | `list` / `probe ` / `register ` / `update` / `delete` | `--auth-type` `none\|bearer\|basic\|oauth2\|url_param`. `--header "Key: Value"` repeatable | | `share add\|list\|remove` | `--user` / `--public` | | `upload` | `--name --zip [--version v1] --env KEY=VAL` | | `upload-github` | `--name --github-url [--version] --env` | | `build-status ` | `-j` | | `logs ` | `--tail` default `200` | | `consumers` / `pin` / `unpin` / `pinned` / `recent` | | | `grant-agent` / `revoke-agent` ` ` | | ## Credentials, OAuth, agent-tools ```bash theme={null} nasiko mcp credential set|status|delete [value] nasiko mcp oauth authorize|status|revoke nasiko mcp agent-tools connectors|enable|disable|tools|rules|set-rule|reset ``` `upload` default version tag `v1`. # Model commands Source: https://docs.nasiko.com/reference/cli/models llm-config and model-registry. Guides: [LLM configs](/models/llm-configs), [Model registry](/models/model-registry). ## llm-config | Command | Notable flags | | - | - | | `create` | `--name --provider --model --fallback` (repeat) `--temperature --max-tokens --api-key-secret --secret-value --pin --pinned-model --default` | | `update ` | Same fields optional; `--clear-fallbacks --no-pin --clear-pinned-model` | | `delete` / `rm ` | `-f/--force` | | `list` / `ls` | `--json` | | `set-default ` | | | `attach ` | `--inbound-format` `openai` \| `anthropic` \| `gemini` | | `detach ` | | | `get ` | `--json` | | `providers` | `--json` | ## model-registry | Command | Flags | Who | | - | - | - | | `ls` / `list` | `--json` | Authenticated | | `set` | `--provider --tier --model` | Superuser. Tier `1` strongest … `3` smallest | # Observe commands Source: https://docs.nasiko.com/reference/cli/observe sessions, traces, spans, project stats, TokenOps dashboard and insights. Guide: [Sessions and traces](/dashboard/sessions-and-traces), [TokenOps](/tokenops/dashboard). | Command | Flags | Purpose | | - | - | - | | `nasiko observe sessions` | `--start-time`, `--json` | List. Default window 7d | | `nasiko observe session ` | `--json` | Detail | | `nasiko observe trace-detail ` | `--json` | Span tree + costs | | `nasiko observe span ` | `--json` | Prompt/completion | | `nasiko observe project-stats ` | `--start-time`, `--json` | Default window 24h | | `nasiko observe finops-dashboard` | `--start-time`, `--json` | TokenOps KPIs. Default 24h | | `nasiko observe insights` | `--start-time` | Narrative insights | `--start-time` is ISO-8601. # Operate commands Source: https://docs.nasiko.com/reference/cli/operate deploy, push, upload, import, ps, logs, lifecycle, deployments, versions, rollback, secrets. Guides: [Deploy](/build/deploy), [Operate](/build/operate), [Versions](/build/versions). ## Ship | Command | Flags | Purpose | | - | - | - | | `nasiko deploy ` | `--name`, `--port` default `8000`, `--env-file`, `-e`, `--writable`, `--writable-path`, `-v`, `-y` | Build + push + deploy | | `nasiko push ` | `--name`, `-v`, `-y` | Build + push, no start | | `nasiko upload [source]` | `-n`, `-v`, `--port` default `8000`, `--env-file`, `-e`, `--writable`, `--writable-path` | Server builds. Default source `.` | | `nasiko import ` | | Cluster pulls `owner/name[:tag]` or `host/owner/name[:tag]` | | `nasiko reupload` | `--id` or `-n`, `[source]`, `-v`, `--changelog` | Rebuild an existing agent | `--writable` mounts persistent storage at `/workspace`. `--writable-path PATH` implies `--writable`. ## Run | Command | Flags | Purpose | | - | - | - | | `nasiko ps` | `-j/--json` | Running agents | | `nasiko logs ` | `-n/--tail` default `50`, `-f/--follow` | Logs | | `nasiko stop/start/restart ` | | Lifecycle. `restart` picks up secrets | | `nasiko scale ` | | Scale (Docker clamps to 1) | | `nasiko rm` | `[id]` or `-n/--name`, `-f/--force` | Terminate + deregister | | `nasiko deployments ls` | alias `list` | Deployments | | `nasiko deployments get ` | | One agent | | `nasiko deployments restart ` | | Restart by deployment ID | | `nasiko versions` | `[id]` or `-n` | History | | `nasiko rollback` | `[id]` or `-n`, `-t/--version`, `--reason` | Roll back | ## Secrets | Command | Flags | | - | - | | `nasiko secrets set ` | `--agent` | | `nasiko secrets get ` | `--agent` | | `nasiko secrets ls` | `--agent` | | `nasiko secrets rm ` | `--agent` | Omit `--agent` for vault-wide secrets. # CLI reference Source: https://docs.nasiko.com/reference/cli/overview Every nasiko command, grouped the way nasiko --help groups them. `nasiko` discovers coding agents, deploys A2A agents, and talks to your Nasiko cluster. Install: [Install and connect](/install). Task guides stay short and link here for flags. ```bash theme={null} nasiko --help nasiko --help ``` State: `~/.nasiko/config.json` (clusters, tokens) and `~/.nasiko/.env` (local `up`). Every command uses the **active cluster** (`nasiko use`). Hidden helpers (`__claude-token`, `__coding-agent-token`, `integration`, `agents report`) are for installed hooks. Don't call them yourself. ## Groups | Group | Page | | - | - | | Setup, auth, budget | [Setup](/reference/cli/setup) | | Coding agents | [Coding agents](/reference/cli/coding-agents) | | Scaffold, skills, card | [Create](/reference/cli/create) | | Deploy, logs, scale | [Operate](/reference/cli/operate) | | Chat and sessions | [Chat](/reference/cli/chat) | | Traces and TokenOps | [Observe](/reference/cli/observe) | | LLM configs | [Models](/reference/cli/models) | | MCP gateway | [MCP](/reference/cli/mcp) | | MAF workflows | [Workflows](/reference/cli/workflows) | | GitHub, registry, agents catalog | [Integrations](/reference/cli/integrations) | | `nasiko-ee` | [Enterprise](/reference/cli/enterprise) | # Setup commands Source: https://docs.nasiko.com/reference/cli/setup up, down, connect, disconnect, use, clusters, status, auth, budget, context-strategy. ## Cluster | Command | Flags | Purpose | | - | - | - | | `nasiko up` | | Start local infra + server. Config: `~/.nasiko/.env` | | `nasiko down` | | Stop the local stack | | `nasiko connect ` | `--name` | Register a cluster by URL and make it active | | `nasiko connect ` | `--agent`, `--config` | [Route](/coding-agents/routing) that harness | | `nasiko disconnect ` | `--force` | Restore harness routing settings | | `nasiko use ` | | Active cluster | | `nasiko clusters` | | List configured clusters | | `nasiko status` | | Cluster health | | `nasiko status ` | | Routing status for one harness | `--name` is only for URLs. `--agent` and `--config` are only for harness targets. ## Auth | Command | Purpose | | - | - | | `nasiko auth login` | Username/password or access key/secret → JWT in `~/.nasiko/config.json` | | `nasiko auth status` | Local expiry check | | `nasiko auth whoami` | `GET /api/users/me` | | `nasiko auth logout` | Best-effort `POST /api/auth/logout`, then clear token | Login, `connect `, and `use` auto-install session reporting for detected harnesses with no install record. See [Discover](/coding-agents/discover). ## Context budget | Command | Purpose | | - | - | | `nasiko budget get` | PACMS tier | | `nasiko budget set ` | Set tier | | `nasiko context-strategy get` | `pacms`, `topk`, or `lastk` | | `nasiko context-strategy set ` | Set strategy | See [Reduce cost](/tokenops/reduce-cost). # Workflow commands Source: https://docs.nasiko.com/reference/cli/workflows nasiko maf workflow and execution. Guide: [MAF workflows](/build/workflows). `--wait` prompts for [HITL](/governance/approvals) inline. ## workflow | Command | Flags | | - | - | | `list` / `ls` | `--json` | | `create` | `--name --description --step` (repeat) `--agent` (repeat; `-` auto-assigns) | | `get ` | `--json` | | `update ` | `--name --description --clear-description`; replace with `--step/--agent`; append `--add-step/--add-agent`; edit `--edit-step` + `--edit-description` / `--edit-agent` | | `delete` / `rm ` | `-f/--force` (soft-delete) | | `run ` | `--wait` | | `executions ` | `--json` | ## execution | Command | Flags | | - | - | | `list` / `ls` | `--json` | | `get ` | `--json` | | `result ` | `--json` | # Config files Source: https://docs.nasiko.com/reference/config-files Where the CLI, server, and coding harnesses keep state. ## CLI and cluster | Path | Purpose | | - | - | | `~/.nasiko/config.json` | Clusters (URL, user, token), `active`, optional `registry_url`. Mode `0600` | | `~/.nasiko/.env` | Local `nasiko up` / server env | | `~/.nasiko/bin/nasiko-cp` | Local server binary for `nasiko up` | | `~/.nasiko/.ee.env` | Enterprise cloud credentials template | | `~/.nasiko/registry-values.json` | Enterprise registry pull token from `nasiko-ee activate` | Env overrides: `NASIKO_REGISTRY_URL`, `NASIKO_REGISTRY_USER`, `NASIKO_REGISTRY_PASS`, `NASIKO_CONTAINER_CLI` (`docker` or `podman`). ## Coding-agent integrations | Path | Purpose | | - | - | | `~/.nasiko/integrations/config.json` | Install records and cluster binding | | `~/.nasiko/integrations/queue/` | Turns waiting for delivery | | `~/.nasiko/integrations/rejected/` | Failed turns | | `~/.nasiko/integrations/watermarks/` | Per-session queue cursor | | `~/.nasiko/integrations/events/` | Partial Codex / Cursor turns | | `~/.nasiko/integrations/report.log` | Hook diagnostics | | `~/.nasiko/integrations/claude.json` | Claude routing state | | `~/.nasiko/integrations/codex-router.json` | Codex routing state | | `~/.nasiko/integrations/opencode-router.json` | OpenCode routing state | Harness files Nasiko may edit: `~/.claude/settings.json`, Codex `config.toml` / `hooks.json`, OpenCode `plugins/*.js`, Cursor `hooks.json`. See [Route models](/coding-agents/routing) and [Session reporting](/coding-agents/session-reporting). ## Agent project | Path | Purpose | | - | - | | `AgentCard.json` | Identity and skills | | `Dockerfile` | Image | | `.nasiko/agent.json` | Bind this directory to a deployed agent ID | | Project `.env` | Loaded by `nasiko run` only | ## Server Compose: `.env` at the repo root. Source run: `server/.env`. Names: [Server configuration](/self-hosting/configuration). # Agent hosting Source: https://docs.nasiko.com/self-hosting/agent-hosting Docker vs Kubernetes hosting, how images reach the cluster, and how server-side builds work. The [lifecycle guide](/build/overview) covers building and deploying agents. This page covers how those containers are hosted. ## The two runtimes **Enterprise feature.** The Kubernetes/KEDA runtime below is `nasiko-ee` only — the open-source `ContainerRuntime` implementation is Docker-only (no Kubernetes feature or dependency at all). `AGENT_RUNTIME=kubernetes` has no effect in an OSS-only install. Selected with `AGENT_RUNTIME`. The Nasiko server's contract is the same either way: deploy, scale, restart, fetch logs, route traffic. Single-replica, reached at `localhost:port` on the Nasiko server host. No cluster to operate — right for local development and single-machine deployments. Deployments and Services, reached at the Service's cluster DNS name. KEDA-driven autoscaling via min/max replica annotations, secrets refreshed before scale-up, in-cluster build pipeline. | Operation | Behavior | | - | - | | Deploy | No-op if the image is unchanged, atomic replace if it changed. Returns observed status immediately — on Kubernetes a pod may still be `Pending`, so poll separately for `Running`. | | Scale | `0` stops the agent. **Under Docker, any count above `1` is clamped to `1`.** Use Kubernetes for horizontal scaling. | | Restart | Docker recreates the container; Kubernetes triggers a rollout restart. Either way, this is how a running agent picks up rotated secrets. | | Status | A missing resource reports `Unknown` rather than erroring, so polling needs no special case. | | Scale-up | Secrets are re-injected before new replicas start — no replica comes up with stale credentials. | Instrumentation is applied uniformly regardless of runtime — see [observability](/dashboard/sessions-and-traces). Agent secrets are AES-256-GCM encrypted at rest and decrypted into a container's environment at deploy time. Plaintext never touches disk outside the running container's memory. ## Two on-ramps, one deployment path ```mermaid theme={null} flowchart LR Dev(["Developer"]) -->|"push a pre-built image"| Reg["Embedded OCI registry
(S3-backed, blob dedup)"] Dev -->|"or upload source"| Build["Build worker"] Build --> Reg Reg --> Deploy["Deploy — instrumentation
+ secret injection"] Deploy --> Run["Running agent container(s)"] ``` * **Push a pre-built image** — `nasiko push` / `nasiko deploy` build locally and push layers and a manifest to the embedded `/v2/*` OCI registry (S3-backed, blob dedup across layers), then request a deployment. * **Upload source** — `nasiko upload` (or `POST /api/agents/upload`) ships an archive; the platform builds server-side, off the request path. Both land in the same place. Deploying wires up networking, injects decrypted secrets, and instruments the container — no change to the agent's code. ## Server-side builds Uploads are queued and picked up by a background worker, so a slow build never holds a client connection. You get a build ID immediately and poll for progress, or watch it on the [Builds screen](/dashboard/agents#builds). * **A failing build can't take others down.** Each build runs isolated from the worker's scheduling loop — a crash resets just that job. * **Retries cap at three attempts.** A job that exhausts them is marked `failed` immediately, so nothing polling hangs forever. * **Orphaned builds are recovered.** A periodic sweep finds jobs still in-progress past any plausible build time — the signature of a replica that died mid-build — and resets or fails them. The same pipeline handles [uploaded MCP servers](/build/mcp/deploy-server), which also get OS-level hardening and network segmentation. In-progress image pushes are buffered on the instance that received them, so a push is sticky to that instance. Behind a load balancer, use session affinity for `/v2/*` traffic or large pushes will fail when split across replicas. ## Related Scaffolding, running locally, deploying, versioning. Deploying, scaling, and watching builds. Traces, cost, and the configuration behind them. Who can deploy, update, or manage an agent. # Backup and restore Source: https://docs.nasiko.com/self-hosting/backup-and-restore What state the platform holds where, and what's on you to back up. Nasiko ships no backup command, snapshot job, or disaster-recovery runbook today. This page tells you what state exists and where, so you can back it up the way you'd back up any stateful service. ## Where state lives The Nasiko server holds no durable in-process state. Everything survives a restart because it lives in one of three external stores. | Store | Holds | Loss impact | | - | - | - | | **Postgres** | Users, agents, access grants, sessions, MAF workflow definitions and history, connector registry and permissions, router decision logs, model pricing | Total — this is the source of truth. Losing it means losing every account, agent record, and grant, even if containers and images survive. | | **S3** (or MinIO, self-hosted) | Agent image layers pushed to the embedded registry, and build contexts staged for in-cluster builds | Images become unrecoverable unless you have the original source or a copy elsewhere. The platform keeps no second copy. | | **Redis** | Flow-guard cascade-limit state, MCP gateway caches, and the MAF execution queue | Mostly safe to lose — cache state is short-TTL and rebuilds from Postgres. The MAF queue is the exception, below. | The server applies its own migrations at startup, so a Postgres instance restored at an older schema version is brought up to date automatically. No manual migration replay. Redis isn't purely disposable. Flow-guard and MCP permission state genuinely are — short TTLs, recomputed from Postgres on demand. But the MAF worker queues executions on a Redis stream, and a queued-but-unclaimed execution has no second copy anywhere. If you run MAF workflows in production, size Redis durability accordingly. ## What to do * **Postgres** — `pg_dump` / point-in-time recovery, or your provider's managed snapshots * **S3 / MinIO** — your object storage's replication or versioning, or cross-region replication for self-hosted MinIO * **Redis** — if you rely on MAF, treat it as a job queue (AOF persistence or managed Redis with replication), not a cache If you provisioned Postgres and MinIO through the self-hosted Helm chart, both run as single-replica StatefulSets on a `PersistentVolumeClaim` with no backup CronJob or snapshot policy. Durability is whatever your storage class provides. For production, prefer a managed database service — it gives you backups, replication, and failover without building them on a self-managed PVC. ## Related Workflow executions are the one thing in Redis that isn't safe to lose. The two runtimes and what each expects from storage. # Server configuration Source: https://docs.nasiko.com/self-hosting/configuration Environment variables for a self-hosted Nasiko server. Names only — never paste secrets into docs or tickets. Copy `.env.example` to `.env` (Compose) or set the same names in `server/.env` / `~/.nasiko/.env` (`nasiko up`). The server reads them at start. This page lists **names**. Do not commit `.env`. ## Required to start | Variable | Purpose | | - | - | | `SECRETS_ENCRYPTION_KEY` | AES-256-GCM key for secrets at rest (`openssl rand -base64 32`) | | `JWT_SECRET` | Signs user session tokens (`openssl rand -base64 48`) | | `ADMIN_USERNAME` / `ADMIN_PASSWORD` | Bootstrap admin. Optional `ADMIN_EMAIL` | | `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_REGION` | Object storage for the embedded OCI registry. Must match the rustfs service when using Compose | | `AGENT_RUNTIME` | `docker` on Compose. `kubernetes` is Enterprise only | | `RUST_LOG` | Tracing filter, e.g. `info` | ## Model routing and agents | Variable | Purpose | | - | - | | `OPENAI_API_KEY` | Default provider key injected for routing-engine and agent-card generation | | `OPENAI_BASE_URL` | Override, e.g. Azure or a gateway | | `OPENAI_MODEL` | Default model | | `AGENT_JWT_SECRET` | Signs short-lived agent JWTs. **Empty ⇒ every router request is 401** | | `LLM_GATEWAY_BASE_URL` | URL deployed agents use to reach the router (Compose sets this) | | `ROUTER_MODEL`, `EMBEDDING_MODEL` | Routing engine models | | `MCP_DESCRIPTION_MODEL` | Fallback for connector descriptions | | `MODEL_PRICING_SYNC_ENABLED` | Sync published rates at boot (default `true`) | ## Observability and coding agents | Variable | Purpose | | - | - | | `TEMPO_URL` | Trace store. Unset with `LOKI_URL` leaves observability off | | `LOKI_URL` | Log store | | `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_PROTOCOL`, `OTEL_EXPORTER_OTLP_HEADERS` | Server and agent export | | `OTEL_SERVICE_NAME`, `OTEL_SAMPLE_RATIO` | | | `CODING_AGENT_OTLP_ENDPOINT` | OTLP/HTTP base for coding-agent traces (server appends `/v1/traces` and `/v1/logs`). Unset: sessions ingest, traces never reach Tempo | ## Flow limits | Variable | Default | | - | - | | `NASIKO_FLOW_MAX_DEPTH` | 5 | | `NASIKO_FLOW_MAX_FAN_OUT` | 20 | | `NASIKO_FLOW_MAX_TOKENS` | 100000 | | `NASIKO_FLOW_TIMEOUT_SECS` | 120 | Also configurable in the dashboard **Settings → Flow limits**. See [Flow limits](/governance/flow-limits). ## Context budgets and compression | Variable | Default | | - | - | | `PACMS_BUDGET_LOW` / `_MEDIUM` / `_HIGH` | 500 / 1000 / 5000 | | `PACMS_HISTORY_POOL_SIZE` | 150 | | `PACMS_HISTORY_MANDATORY_RECENT` | 3 | | `TOKEN_COMPRESS_TOOL_RESULTS` | `true` | | `TOKEN_COMPRESS_TOOL_RESULTS_MIN_BYTES` | 2048 | | `TOKEN_COMPRESS_HISTORY` | `true` | | `TOKEN_COMPRESS_HISTORY_MIN_BYTES` | 2048 | See [Reduce cost](/tokenops/reduce-cost). ## MCP gateway | Variable | Purpose | | - | - | | `COMPOSIO_API_KEY` | Enables Composio toolkits. Unset: custom MCP still works | | `COMPOSIO_BASE_URL`, `COMPOSIO_WEBHOOK_SECRET` | | | `SEED_TOOLKITS` | Comma-separated toolkits to register at boot (no-op without the API key) | | `MCP_GATEWAY_PUBLIC_URL` | Injected into agents as `MCP_GATEWAY_URL` | | `MCP_OAUTH_REDIRECT_BASE_URL`, `COMPOSIO_CALLBACK_BASE_URL` | Browser OAuth redirects | | `MCP_UPLOAD_MAX_BYTES` | Default 50 MiB | | `MCP_UPLOAD_DEFAULT_PORT`, `MCP_SERVERS_NETWORK`, `MCP_UPLOAD_MAX_REPLICAS` | Uploaded MCP servers | ## Source-run connection URLs Used when the server is not the Compose `server` service: | Variable | Typical local value | | - | - | | `DATABASE_URL` | `postgres://nasiko:nasiko@localhost:5432/nasiko_dev` | | `REDIS_URL` | `redis://localhost:6379` | | `S3_ENDPOINT` | `http://localhost:9000` | | `DOCKER_AGENT_NETWORK` | Compose network name | | `OCI_REGISTRY_HOST` | Host agents pull from | | `APP_BASE_URL` | Public URL of this server | | `CP_BIND` | Default `0.0.0.0:8080` | ## SSO, GitHub, multi-tenant, imports | Variable | Purpose | | - | - | | `OIDC_ISSUER_URL`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, `OIDC_REDIRECT_URI` | OIDC. Login redirect is Enterprise — see [SSO and SCIM](/governance/sso-and-scim) | | `OIDC_SCOPES`, `OIDC_PROVIDER_LABEL`, `OIDC_ALLOWED_REDIRECT_ORIGINS`, `OIDC_CENTRAL_CALLBACK_URL` | | | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GITHUB_CALLBACK_URL` | Import-from-GitHub | | `MULTI_TENANT_MODE`, `NASIKO_BFF_URL`, `ALLOW_PERSONAL_EMAILS` | Hosted / multi-tenant | | `REGISTRY_IMPORT_ALLOWED_HOSTS` | Comma-separated hosts allowed for `nasiko import`. Empty rejects all | | `GIT_CLONE_ALLOWED_HOSTS` | Default github.com, gitlab.com, bitbucket.org | | `CORS_ALLOWED_ORIGINS` | Empty = same-origin | | `SEED_AGENTS` | Space-separated images to deploy on boot | | `TENANT_ID` | Optional tenant label | Full behavioral reference for flow and hosting: [Flow limits](/governance/flow-limits), [Agent hosting](/self-hosting/agent-hosting). # Deploy the stack Source: https://docs.nasiko.com/self-hosting/deploy Run Nasiko with Docker Compose or nasiko up, and what each service is for. Two local paths. Docker Compose runs the full stack as containers. `nasiko up` runs infrastructure in Docker and the Nasiko server as a local process. A remote cluster is just a URL — see [Install and connect](/install). ## Docker Compose (recommended) From a clone of the [nasiko](https://github.com/Nasiko-Labs/nasiko) repo: ```bash theme={null} cp .env.example .env ``` Set at least: * `ADMIN_PASSWORD` — password for the `admin` account * `OPENAI_API_KEY` — a model provider key * `AGENT_JWT_SECRET` — uncomment and set (`openssl rand -base64 48`) if you will [route coding agents](/coding-agents/routing) * `SECRETS_ENCRYPTION_KEY` and `JWT_SECRET` — already filled with dev values in the example; replace them outside a laptop trial ```bash theme={null} docker compose up -d ``` The dashboard is [http://localhost:8080](http://localhost:8080). Sign in as `admin` with `ADMIN_PASSWORD`. First start compiles the server image and takes several minutes. ```bash theme={null} docker compose logs -f server docker compose down ``` Compose sets `TEMPO_URL`, `LOKI_URL`, `CODING_AGENT_OTLP_ENDPOINT`, `MCP_GATEWAY_PUBLIC_URL`, and `LLM_GATEWAY_BASE_URL` for you. That is why coding-agent traces and model routing work on Compose without extra exports. ## Services and ports | Service | Host port | Role | | - | - | - | | `nasiko-server` | **8080** | API, dashboard, LLM router, MCP gateway, OCI registry | | Postgres (pgvector/pg16) | **5432** | Catalog, sessions, TokenOps, secrets | | Redis | **6379** | Flow guards, caches | | rustfs (S3) | **9000** | Images, uploads, OCI blobs | | OpenTelemetry Collector | **4317** gRPC, **4318** HTTP | Traces and logs from the server and coding agents | | Tempo | **3200** | Trace store | | Loki | **3100** | Log store | Network name: `nasiko`. Agent containers join it. For a Rust developer loop (infra in Docker, server via cargo): `docker compose -f docker-compose.infra.yml up -d`, then `just run` / `just dev`. Same ports; the server binds 8080 on the host. ## nasiko up ```bash theme={null} nasiko up nasiko down ``` Starts Compose infrastructure and runs the server from `~/.nasiko/bin/nasiko-cp` (pulled as `nasiko/cp:latest` if missing). Config is `~/.nasiko/.env`. The CLI registers the cluster as `local`. `nasiko up` does **not** set `ADMIN_PASSWORD`, `JWT_SECRET`, `AGENT_JWT_SECRET`, `TEMPO_URL`, `LOKI_URL`, or `CODING_AGENT_OTLP_ENDPOINT`. Export them in that shell, or use Compose. The server is a child of the terminal — closing it stops Nasiko. Details: [Install and connect](/install#troubleshooting-nasiko-up). If the image pull fails, build from source: ```bash theme={null} cargo build --release -p nasiko-server mkdir -p ~/.nasiko/bin cp target/release/nasiko-server ~/.nasiko/bin/nasiko-cp ``` ## Next Environment variables. What to snapshot. # Cost attribution Source: https://docs.nasiko.com/tokenops/attribution One cost schema across harnesses, frameworks, models and teams — and why every call is attributed. Every model call that goes through Nasiko is attributed before it is served. Unattributed usage is rejected, not billed later. That is what lets TokenOps join spend across vendors whose own invoices cannot be joined. ## What is attributed A cost row always has: | Dimension | Comes from | | - | - | | **Agent** | The coding-agent identity or the deployed agent that made the call | | **Developer** | The logged-in user who started the flow | | **Session** | The chat or harness conversation | | **Model and provider** | The outbound model the [LLM router](/models/llm-router) selected | | **Workflow** | The MAF workflow, when the call ran as a workflow step | Team and tenant dimensions exist when an [organization](/governance/organizations) is configured (Enterprise). On an open-source cluster, spend still resolves to a person and an agent. The routing engine's agent-selection call is a separate line item. It is never folded into the agent it picked. ## Strict attribution Two identities ride on every model call and every MCP `tools/call`: | Identity | Carrier | | - | - | | **Agent** | A short-lived JWT (`Authorization: Bearer` or `x-api-key`), minted for that agent | | **User / session** | W3C `traceparent`, whose trace ID is the flow ID | Nasiko writes a flow record before the call is dispatched. The router looks up that flow. If the header is missing, malformed, names no live flow, or names a flow this agent is not part of, the call is **rejected with 403**. Embeddings (`POST /v1/embeddings`) use the same rule. Consequence: an agent cannot spend tokens outside a user flow, and cannot spend against another user's flow. MCP tool calls are held to the same standard, because permissions and flow token limits key off that user identity. Coding-agent routed calls use the coding-agent identity created at [registration](/coding-agents/discover#what-registration-creates). Reported sessions attach to the same identity, so harness spend and routed spend roll up together. ## Cache-aware accounting Provider cache hits (prompt cache reads and writes) are recorded as their own token counts, not billed as fresh input. TokenOps will not overcharge a cached prompt as if it were sent in full. ## TokenOps API The product name is **TokenOps**. The HTTP path is `finops`. | Method | Path | Purpose | | - | - | - | | `GET` | `/api/observability/finops/dashboard` | KPI strip and period totals | | `GET` | `/api/observability/finops/spend-timeseries` | Spend over time | | `GET` | `/api/observability/finops/spend-calendar` | Month grid | | `GET` | `/api/observability/finops/spend-calendar/day` | One day's hourly drilldown | | `GET` | `/api/observability/finops/attributions` | Per-agent or per-workflow table | | `GET` | `/api/observability/finops/agent-hours` | Agent hours | | `POST` | `/api/observability/finops/insights` | Short narrative over a KPI snapshot you send | | `GET` | `/api/usage/summary` | Usage summary | | `GET` | `/api/usage/history` | Usage history | | `GET` | `/api/usage/by-agent` | Usage by agent | | `GET` | `/api/usage/by-model` | Usage by model | All of these require a login. The dashboard **Overview** and **TokenOps** screens call the same routes. CLI: `nasiko observe finops-dashboard` and `nasiko observe insights`. There are no spend caps or alerts. Flow token limits ([flow limits](/governance/flow-limits)) and [context budgets](/tokenops/reduce-cost) are the limits that ship. ## Related The screens that plot these numbers. Where rates come from. # TokenOps dashboard Source: https://docs.nasiko.com/tokenops/dashboard Read spend, tokens, latency and attribution on the Overview and TokenOps screens, and export what you see. Two dashboard screens read the same TokenOps data. **Overview** (`/`) is the landing screen: fleet health, activity and spend on one page. **TokenOps** (`/tokenops`) is the cost view: what you spent, where it concentrated, and which agents or workflows drove it. Both screens take a period and a time range, compare every headline figure against the previous window, and export the attribution table as CSV. ## Filters both screens share | Control | What it does | | - | - | | **Period** | The current month or any of the previous five. A past month is bounded to that month, not "that month through today". | | **Time range** | **24h**, **7d** or **30d**. Narrows the window back from the end of the selected period (or from now, for the current month). | | **Org unit** | Scopes the screen to the users in one unit of your organization. | **Enterprise.** The **Org unit** filter needs an organization hierarchy and manager-level access or above. On open-source clusters, or for users without that access, it stays disabled with a tooltip explaining why. See [organizations](/governance/organizations). Every KPI carries a movement chip: the change against the previous window of the same length. The arrow shows the direction; the color shows whether that direction is good news. Rising spend is an up arrow in a warning color. ## Overview The Overview screen answers "is the fleet healthy, and what is it costing?" in one place. ### Filter bar Alongside **Period**, **Time range** and **Org unit**, Overview adds a **Scope** control: * **Org** (default) counts every agent you can see. * **Myself** narrows the KPI strip and the attributions table to agents you own. The activity, performance and spend charts stay fleet-wide, and the screen says so under the filter bar while **Myself** is selected. **Myself** means agents you *own*, not traffic you generated. If you mostly use agents other people deployed, **Myself** reads close to zero. ### KPI strip | Tile | What it shows | | - | - | | **Agents** | Agents deployed in the workspace | | **Active** | Agents that ran in the window, out of the total | | **Runs** | Operations in the window; the tooltip adds the last-24-hours count and tool calls | | **Spend** | Spend and tokens together, for example `$412/18.2M` | | **Avg latency** | Median (p50) latency across every operation; the tooltip adds p95 and p99 | The gap between **Active** and **Agents** is often the most useful number on the screen: it counts agents that are deployed but idle. ### Panels * **Agent activity** plots calls over time. Switch between **All**, **Agent calls** (one per trace) and **Tool calls** (tool-call spans inside those traces). * **Performance** plots p50, p95 and p99 latency over time. When the gap between p95 and p99 moves, a note under the chart reports it, for example "Latency tail widened 74% since Wednesday". * **Spend over time** stacks each bucket's heaviest agent against everyone else. Toggle **\$** for dollars or **%** for each bucket's share of the window. The **Agent** and **Provider** selects in this panel filter this panel only. * **Attributions** lists every agent with **Runs**, **Tokens**, **Spend**, **Cost/Op** and **Avg latency**. Hover **Runs** for tool-call counts and **Avg latency** for p95/p99. Sort by **Most run**, **Highest spend**, **Most tokens**, **Slowest (p50)**, **Slowest (p95)** or **Name**. Buckets are hourly for short windows and daily for longer ones. With no agents deployed, Overview shows a first-run screen with **Import an agent** and **Explore Artifact Library** instead of empty charts. ## TokenOps The TokenOps screen is the cost view. Every filter on it reloads the whole screen. ### Filter bar Alongside **Period**, **Time range** and **Org unit**: | Filter | Scope | | - | - | | **Agent** | One agent, including a registered coding agent | | **Provider** | One provider from the [LLM router catalog](/models/providers) | | **Model** | One model id | Picking a month makes the month the window. Picking a time range after that returns to a range anchored on now. ### KPI strip | Tile | What it shows | | - | - | | **Total AI spend** | Spend for the window. The tooltip shows how much of it is estimated and how many operations had unknown pricing confidence. | | **Total tokens** | Tokens across all agents | | **Cost / operation** | Spend divided by operations; the tooltip adds agent hours | | **Avg latency** | Average latency; the tooltip shows how many agents were active | A tile with no previous window to compare against shows no chip, rather than a misleading 0%. An estimated figure is a real cost priced at a fallback rate, not a guess at usage. See [how prices are determined](/tokenops/pricing) for when a call is marked estimated. ### Spend over time Spend (left axis) and operation count (right axis) per bucket. Buckets are hourly for short windows and daily for longer ones. ### Spend concentration A day grid for the selected month sits above an hourly chart for one day. Click a day to drill into it. * Each hour's bar is stacked by that day's top four agents, plus **Others**. * The dashed line is the day's average hourly spend, also written under the chart ("Averaging \$3.20/hour on 2026-09-14"). * The legend lists the top agents with their spend for the day. The day picker follows the **Period** select: a past month opens on its last day, the current month on today. It ignores **Time range**, but honors the **Agent**, **Provider** and **Model** filters. Future days are disabled. ### Attributions Toggle **Agent** or **Workflow** to attribute spend to deployed agents or to [MAF workflows](/build/workflows). | View | Columns | | - | - | | **Agent** | Agent, Spend, Tokens, Output, Input, Operations, Avg cost/op, Agent hours, Avg latency | | **Workflow** | Workflow, Spend, Tokens, Operations, Avg latency | Search by name, and sort by **Highest spend**, **Most tokens**, **Most operations**, **Slowest**, **Most agent hours** (agent view) or **Name**. A `~approx` badge marks a very high-volume agent whose figures are approximate and may undercount. The split between **Input** and **Output** is the practical cost lever. A high input share usually means context is being resent; see [reduce cost](/tokenops/reduce-cost). A high output share usually means verbose answers, which a cheaper model or a brevity directive can address. ## Export **Export report** downloads the attributions table as CSV: `overview.csv` from Overview, `tokenops.csv` from TokenOps. The file follows the current filters, view and sort order. On TokenOps it includes every row in the window, not only rows matching the table's search box. ## Where the numbers come from Every model call routed through the [LLM router](/models/llm-router), every orchestrator call, and every reporting [coding agent session](/coding-agents/session-reporting) records its token counts. Nothing is added to your agent code. Costs apply those counts to the price in effect when the call happened, so a later price change never re-costs last month. * [Cost attribution](/tokenops/attribution) explains the dimensions and why every call is attributed. * [Pricing](/tokenops/pricing) explains where rates come from. The routing engine's agent-selection call is metered as its own line, not folded into the agent it picked, so routing never inflates an agent's numbers. For a short written summary instead of raw figures, `POST /api/observability/finops/insights` returns up to three insight bullets over a KPI snapshot you send it. Useful for a weekly digest. See the [API table](/tokenops/attribution#tokenops-api). ## Related One cost schema across vendors, harnesses and teams. Compression, context budgets and cheaper models. Drill from a cost figure into the traces behind it. Change which models your agents are routed to. # Pricing Source: https://docs.nasiko.com/tokenops/pricing Where TokenOps rates come from, how estimated costs are marked, and how custom providers are priced. Token counts become dollars by looking up a per-model rate that was in effect when the call happened. A later price change never re-costs last month. ## Rate lookup For each model Nasiko knows: 1. A versioned **price book** row: input and output rate per million tokens, with an effective-date window. 2. If no row matches, a **built-in fallback table** is used and the cost is marked **estimated**. 3. Nasiko never reports a null cost. Unknown rates still produce a number, flagged as estimated. The TokenOps KPI tooltip shows how much of the window is estimated and how many operations had unknown pricing confidence. An estimated figure is a real cost priced at a fallback rate, not a guess at usage. Cache-read and cache-write tokens use the provider's cache rates when the book has them. ## Where the book is filled On an open-source cluster the book is seeded with the models Nasiko ships. Optionally, at boot Nasiko can sync published rates from the Portkey price book into `model_pricing`. That sync is fail-open: if it cannot reach the network, existing rows stay. | Variable | Default | Purpose | | - | - | - | | `MODEL_PRICING_SYNC_ENABLED` | `true` | Sync published rates at boot | Custom providers you register (LiteLLM, vLLM, Ollama, Azure, Bedrock, OpenRouter, an internal gateway) need a rate before TokenOps can price them precisely. Until a row exists they fall through to the built-in table and show as estimated. Superusers add mappings with the [model registry](/models/model-registry) and provider catalog; see [Providers](/models/providers). ## CLI ```bash theme={null} nasiko llm-config providers # catalog with pricing where known nasiko observe finops-dashboard nasiko observe insights ``` `providers` is the same catalog the LLM router screen uses. Models listed without a rate still route; their TokenOps line is estimated until a rate exists. # Reduce cost Source: https://docs.nasiko.com/tokenops/reduce-cost Compression, context budgets, caching and routing cheaper models — the cost levers that ship today. TokenOps tells you what you spent. These are the settings that change what you spend next. None of them require a wrapper in your agent code. There are no spend caps or alerts. The limits that ship are flow token budgets and per-user context budgets. ## Route to a cheaper model The largest lever is which model a call is routed to. Attach an [LLM config](/models/llm-configs) with a smaller model, or set fallbacks so a failure retries cheaper rather than more expensive: ```bash theme={null} nasiko llm-config create --name cheap --provider openai --model gpt-4o-mini \ --api-key-secret OPENAI_API_KEY nasiko llm-config attach my-agent cheap nasiko connect claude --config cheap ``` Reasoning-level configs on the [LLM router](/models/llm-router) screen assign a fast model to simple work and a stronger model to hard work. ## Compress payloads Nasiko can shrink tool results, JSON, logs, diffs and history before they are sent back into the model. Compression is structural (it keeps the shape of the data) and fails closed: if a compressor errors, the original payload is sent. **Per agent.** On the agent's card in the dashboard, the token-optimization switch sets `compress_enabled`. It is off until you turn it on. The same field is on `PUT /api/agents/{id}`. **Cluster-wide defaults** (self-hosting) are kill switches, not enablers. They default on so a dashboard toggle takes effect without a restart. Set one to `false` to stop that layer everywhere. Compression still does not run until the agent (or, for orchestrator history, every live agent you own) has `compress_enabled`. | Variable | Default | What it compresses | | - | - | - | | `TOKEN_COMPRESS_TOOL_RESULTS` | `true` | Tool results in the orchestrator ReAct loop | | `TOKEN_COMPRESS_TOOL_RESULTS_MIN_BYTES` | `2048` | Skip payloads smaller than this | | `TOKEN_COMPRESS_HISTORY` | `true` | Conversation history the orchestrator keeps between turns | | `TOKEN_COMPRESS_HISTORY_MIN_BYTES` | `2048` | Skip history smaller than this | A high **Input** share on the TokenOps attributions table usually means context is being resent. Compression and the context budget below are the two ways to cut that. ## Context budget and selection Each user has a PACMS budget tier and a context-selection strategy. They control how much chat history a request carries, not how much the cluster may spend overall. ```bash theme={null} nasiko budget get nasiko budget set medium # low | medium | high nasiko context-strategy get nasiko context-strategy set pacms # pacms | topk | lastk ``` | Tier | Default token budget | | - | - | | `low` | 500 (`PACMS_BUDGET_LOW`) | | `medium` | 1000 (`PACMS_BUDGET_MEDIUM`) — default | | `high` | 5000 (`PACMS_BUDGET_HIGH`) | | Strategy | What it keeps | | - | - | | `pacms` | Relevance-scored history, plus a few most-recent messages always | | `topk` | The highest-scoring messages in the pool | | `lastk` | The most recent messages only | API: `GET`/`PATCH /api/me/pacms-budget` and `GET`/`PATCH /api/me/context-strategy`. ## Provider prompt cache When the outbound provider supports prompt caching, Nasiko records cache-read and cache-write tokens separately. Cached input is not billed as fresh input. You do not configure this; it follows the provider. ## What is not here * Spend caps, alerts, chargeback, and kill switches are not a product surface. * Flow-level token, depth, fan-out and timeout limits *are* — see [Flow limits](/governance/flow-limits).