Skip to main content

ONE CLI, EVERY ENVIRONMENT. ONE BINARY, ONE DATABASE.

Identity, RBAC, audit_events, and MCP subprocess lifecycle run in one Rust process against one PostgreSQL. The operator types into the same CLI from a laptop that the server answers from in production, with no separate IdP, PDP, or log shipper on the call path.

One Binary, Four Control Surfaces

The usual AI stack is four vendors pretending to be one. An identity provider issues tokens, a policy decision point evaluates rules, a log shipper sends events to a SIEM, and an orchestrator restarts MCP servers when they crash. Each one is its own deployment, its own upgrade cadence, its own attack surface. A CISO auditing an agent incident reads four log formats and argues about which clock is correct.

systemprompt.io collapses those four into one process. The CLI is the operator surface. Its binary is the same binary that runs the control plane. The PostgreSQL behind it holds identity, policy state, and the audit log in one database. Authentication happens inside the binary, not in front of it. Scope and role checks run in-process, not in a sidecar. Tool-call audit rows land in the same database as the session they came from, written inline by the dispatcher. MCP server supervision is a module in the same binary, not a Kubernetes operator.

The operator surface lives behind one grammar. The systemprompt CLI exposes command domains for the operations that have to happen day-to-day, from skills and content through users and RBAC through deployment and analytics. A staff engineer adding a feature is not choosing between four runtimes. The binary the CI builds is the binary that serves, audits, and governs. The build-vs-buy delta for a CTO is not "should I build this" but "how many services did consolidation take out of my stack", and the answer, enumerated below, is at least four.

  • No IdP To Operate Alongside — Authentication, session issuance, and JWT validation run inside the binary. An organisation with an existing SSO fronts the binary with a proxy, but no second identity service has to be upgraded, patched, or paged on at 3am.
  • No Policy Service To Call — Role checks, scope checks, and rate limits run in the same process as the tool dispatcher. A denied call is a function return, not a gRPC timeout. No OPA cluster needs keeping in sync with the binary it is gating.
  • No Audit Shipper, No Orchestrator — Every decision writes a structured row to the same PostgreSQL that holds sessions and permissions. A SIEM reads the database or consumes the event stream directly. MCP server lifecycle (spawn, health, restart) is a module in the same binary, not a separate control loop.

One RBAC Path For CLI And Agents

Most agent integrations end up as a parallel API. The agent gets a thin wrapper with fewer permissions, a separate audit log, and a different failure mode. Two months in, the wrapper has drifted from the real operator surface, and the audit trail has two formats no compliance officer wants to reconcile.

In this codebase, the MCP servers the agents talk to share the same authentication and authorisation path as the CLI. Every tool call, whether a human typed it or an agent dispatched it, passes through the in-process permission check before the handler runs. The middleware, the role model, and the audit row are the same for both callers. An auditor asking "who called this tool and under which identity" gets one table and one query, not a correlation exercise.

Tool discovery is a runtime call, not a hand-maintained document. The MCP server returns whatever the tool registry holds at startup, so an agent bound to the server learns what is callable on this deployment without a release. The CISO pre-answer lives in the audit schema. Every decision, allow or deny, writes a row with the caller's identity. The CTO pre-answer lives in the binary. The surface the agent sees is the surface the human sees, because there is only one surface.

  • One RBAC Path, Two Callers — The same permission middleware gates every MCP tool call the binary serves. An agent with a leaked token and a human with the same token hit the same denial, and the audit row does not care which end of the connection typed the command.
  • Typed Tool Schemas At Runtime Discovery — Tools ship JSON Schema for input and output. Agents bind to the schema, not to a hand-written guide, and a server restart is a fresh discovery call rather than a coordination email. A new tool becomes callable without a wrapper update.
  • One Audit Log, Not Two — Human CLI commands and agent tool calls write to the same structured audit trail with the same identity fields. No second log format to reconcile, no second compliance pipeline to keep current.

Four-Case Profile Resolver

Run a command against the wrong environment once and the lesson sticks. Most CLIs make that easy. A stale shell variable, a forgotten Kubernetes context, an SSH tunnel left open. The blast radius lives in operator memory, not in the tool.

In this binary, the target is resolved through one explicit cascade. The session resolver checks the command-line profile flag first, then the profile environment variable, then the active session stored on disk, then a default bootstrap profile. Four cases in order, all in one function in source. The staff engineer pre-answer is a file path and a line range. The rule is in the code, not in shell convention.

A profile is a YAML file holding the database URL, server endpoints, secrets, and runtime settings for one target. Switching profiles changes the next command's destination without changing the audit format the destination writes, so a query against local PostgreSQL and a query against a production deployment land in the same shape at the end. Login uses OAuth against the cloud control plane rather than SSH-and-copy-paste, so credentials never live in shell history.

  • One Resolver, Four Cases, In Source — The resolver consults the command-line flag, then the environment variable, then the active session, then the bootstrap default. The order is fixed in code the reader can open, not in a deployment runbook that drifts.
  • Profiles Are Files, Not Shell State — Every target lives in a profile YAML with its own database URL, endpoints, and secrets. Reviewing what production points at is reading one file, so a handover does not require walking a new operator through a shell history.
  • OAuth Login, Not SSH Tunnels — Cloud auth handles login, token refresh, and identity against the control plane. No bastion host, no shell alias to remember, no credentials pasted into a terminal in front of a camera.

Linker-Time Extensions

An infrastructure library that cannot be extended becomes a ceiling. One with a central extension registry becomes a coordination tax. In this codebase, an extension is a module that declares the surface it contributes (router, jobs, migrations, schemas, assets, providers) and overrides only the pieces it actually adds. Defaults handle the rest. The web extension contributes page providers and content ingestion. A smaller extension might contribute only a schema. Both register the same way.

Registration requires no central list. An extension announces itself at link time, and a startup discovery pass enumerates everything the linker kept. Adding an extension means adding a crate and dropping one registration line into it. Removing one means removing the crate. No registry file to edit, no coordination with a platform team on a manifest that has to merge cleanly, no runtime reflection that could silently miss an extension at load time.

A malformed config does not reach a running binary. Configuration under services/ is parsed at compile time, so a broken YAML fails the build, not a customer call. The staff-engineer pre-answer is the extension module and the discovery pass named in the references below. The CTO pre-answer is that consolidation did not come with a plugin-manager service to keep alive.

  • One Trait, Defaults For Most Methods — The extension surface covers router, jobs, migrations, schemas, assets, and providers, with defaults for almost every method. An extension overrides only the methods that pull weight, so there is no boilerplate tax on a small contribution.
  • Linker-Time Registration — Extensions register at link time through one macro call. Adding a crate and calling the macro is all it takes. Removing the crate removes it. No central YAML to edit, no coordination merge to unblock a release.
  • Broken YAML Fails The Build — Declarative configuration under services/ is parsed at compile time, so a malformed file never reaches a running binary. The CI that accepts a pull request is the same validation an operator would run locally, by design.

Compile-Time Config Validation

The operator problem with most stacks is not configuration. It is the gap between configuration and effect. A YAML file lives in one repo, a deployment lives in another, a job runner is somewhere else, and the audit log of who flipped what lives in a fourth place. Reconciling them is the work.

In this codebase, declarative state lives under the services/ tree. Agents, MCP servers, skills, web navigation, homepage, features all live there. The build script parses every file at compile time, so a malformed YAML never reaches a running binary. A compile-time error shows up on the pull request a reviewer sees, before the change lands in front of a customer.

Background work is part of the same binary, not a separate runner. Each extension contributes its own list of jobs. Each job declares a name and a cron schedule. The scheduler that runs them is a module in the binary. The CISO pre-answer is the structured log entry. Jobs write to the same audit table as interactive commands, so "who ran this and why" has one query. The CTO pre-answer is consolidation. No sidecar runner to page on, no second container to authorise, no separate retention policy to set.

  • YAML Validated At Compile Time — Edit a file under services/, run the matching sync command. The build script validates every YAML at compile time, so a broken file fails CI rather than the binary.
  • Jobs Live In The Binary — Each extension contributes its own jobs. Each job declares a name and a cron schedule. The scheduler is a module in the same binary. No sidecar runner, no second container to deploy alongside the server.
  • Logs And Tracing Behind One CLI — The infra logs commands cover stream, search, summary, export, and full audit reconstruction. An auditor rebuilds an AI request from one terminal without logging into a separate observability service.

Founder-led. Self-service first.

No sales team. No demo theatre. The template is free to evaluate — if it solves your problem, we talk.

Who we are

One founder, one binary, full IP ownership. Every line of Rust, every governance rule, every MCP integration — written in-house. Two years of building AI governance infrastructure from first principles. No venture capital dictating roadmap. No advisory board approving features.

How to engage

One process. Four control surfaces. No sidecars.

Clone the template. Build the binary. Identity, RBAC, audit, jobs, and MCP lifecycle run behind one CLI against one database.