Skip to content

SarthiAI/Niyam

Repository files navigation

Niyam

Niyam

AI governance from prompt to proof

Self-hosted AI governance in one binary. Niyam sits in front of every model call, gates what AI agents are allowed to do, keeps sensitive data out of prompts and responses, and records every decision in a post-quantum-signed audit chain that a central control plane re-verifies. Security authors the rules in one place; every team's AI enrolls to that authority and cannot weaken it locally.

No SaaS. No data leaves your network. One niyam binary runs the central control plane (with an embedded web console) or the data-plane enforcement, selected at startup. One SDK embeds the same gate in your own agent code.

Two integration points: set your model client's base URL to the gateway, and wrap your tool-dispatch in gate.dispatch(...). Policy, content safety, audit, and revocation are then enforced centrally and recorded, without changing the rest of your app.

import niyam

gate = niyam.GovernedGate("http://control.internal:9090", "enrollment-token", "billing-agent")

decision = gate.dispatch(capability_token, "tools.payment.refund", "orders/8842")
if decision.allowed:          # decided in-process, recorded in a signed audit chain
    issue_refund()

Grounded facts

  • One mode-selected static binary. The web console is compiled into it.
  • Deterministic enforcement in the hot path. Models run only at the edges (content classifiers), never in the allow/deny decision.
  • Post-quantum from day one: every signature and audit entry is ML-DSA-65 hybrid with Ed25519.
  • SQLite by default (no database to run, one file to back up). Postgres for large fleets and HA.
  • Measured on real cloud hardware: 58,000 to 77,000 policy checks per second on 4 cores; control-plane memory stays around 20 MB and flat while the audit record grows to gigabytes.
  • Elastic License 2.0. Self-hosted only.

Contents


The problem

When an enterprise adopts AI, it has to answer the same operational questions for every team and every app:

  1. Who may call which model, and at what spend?
  2. What is an AI agent allowed to actually do: send an email, move money, delete a record?
  3. How do PII, PHI, and secrets stay out of prompts and out of responses?
  4. Can you prove, later and to an auditor, exactly what every AI did and why?
  5. Does any of that map to ISO 42001, the EU AI Act, or NIST AI RMF?
  6. Can security set these rules centrally, so one team cannot quietly turn them off?

The common answer is five to seven separate tools, wired in per application, that mostly log rather than block, and that any team can route around. That is observability, not governance. Niyam is the single product that enforces all six, centrally, in real time, and records the proof.

What Niyam does

The requirement How Niyam meets it Piece
One place to set the rules Policy authored once, compiled, and distributed to every node Lipi + control plane
Enforce the model call An OpenAI-compatible gateway authenticates, gates, budgets, and forwards Marg
Enforce the action A deterministic gate + holder-bound capability tokens decide what agents may do Kavach + Mudra
Protect the data Prompt-injection, PII/PHI redaction, and output moderation on request and response Drishti
Prove everything Every decision lands in one post-quantum-signed audit chain, re-verified centrally Kavach audit
Map to the law Signed, auditor-ready evidence bundles per framework Pramana
Test models continuously Signed model and agent evaluation evidence Pariksha
Central governs, teams cannot weaken it Every node and SDK enrolls, pulls policy, and pushes audit; nothing overrides locally Control plane

How it works

Two planes and one rule: the centre decides, the edges enforce and prove.

                    +-----------------------------------------------+
                    |                 CONTROL PLANE                 |
                    |   policy   |   keys   |   capability issuer   |
                    |   audit aggregation + independent re-verify   |
                    |   embedded web console  +  admin API          |
                    +-----------------------------------------------+
                          ^   enroll / pull policy / push audit
                          |   (nodes and SDKs cannot weaken it)
          +---------------+----------------+---------------------+
          |               |                |                     |
    +-----------+   +-----------+    +---------------+    +---------------+
    |  Gateway  |   |  Gateway  |    | Safety server |    |   SDK gate    |
    | + safety  |   | + safety  |    |    (GPU)      |    | in your app   |
    +-----------+   +-----------+    +---------------+    +---------------+
     model calls     model calls      shared content       your agent's
     actions, data   actions, data    safety for a fleet    actions
  • The control plane holds the policy, the keys, the capability-token issuer, and the trust root. It aggregates every node's audit chain and re-verifies it independently. It serves the console and the admin API. Data-plane nodes and SDKs enroll to it, pull policy on a timer, and push their signed audit. They cannot author or relax policy.
  • Data-plane nodes are the same binary in an enforcing mode: the gateway, content safety, and the action gate. They fail static: if the centre is unreachable they keep enforcing on the last policy they pulled and buffer their audit until it returns.
  • Deterministic in the hot path. Every allow/deny is plain code. The only models in the system are Drishti's small content classifiers at the data boundary and build-time policy authoring, never a model deciding a request in real time.
  • Post-quantum from day one. Every audit entry and token is signed ML-DSA-65 hybrid with Ed25519. The control plane pins each node's public bundle at enrollment and re-verifies every chain it receives against it.

The three enforcement boundaries

Every AI touchpoint is blockable in real time, not merely logged.

The model call (the gateway)

Point any OpenAI-compatible client at the gateway. Each call is authenticated with a minted key, checked against policy (which models, which principals, what budget), recorded, and forwarded to your provider.

curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer $NIYAM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'

Adoption is one line: set the client's base URL to http://localhost:8080/v1. An unknown or revoked key is 401; a model the policy does not permit is refused.

The action (the capability gate)

Actions are allowed or denied by policy in deterministic code. Agents carry capability tokens that are holder-bound (a bare bearer token is not enough), scoped to specific actions and resources, and revocable centrally within one refresh interval. The gate enforces on the gateway's live traffic and, identically, inside the SDK.

The data (content safety)

Three checks run on the request and the response: prompt-injection detection, PII/PHI redaction, and output-safety moderation. If the safety service is ever unreachable, the gateway blocks the call rather than letting it through ungoverned (the default; you can opt out).

prompt injection ->  422 blocked
"email a@b.com"  ->  forwarded as "[EMAIL]"   (masked before it reaches the provider)
unsafe response  ->  422 blocked

Safety runs embedded on the gateway's CPU, or as a shared safety-server (put it on a GPU) that a fleet of gateways calls. The decisions are identical either way.

Quick start

Stand up a control plane and a governed node with Docker:

cd deploy
docker compose up --build

This starts the control plane (console and admin API on port 9090) and one data-plane node in the full profile (governed gateway on port 8080), enrolled to the control plane. Open the console at http://localhost:9090, sign in with the admin token, author a policy, mint a client key, then point an OpenAI-compatible client at http://localhost:8080/v1.

Govern your own code: the SDK

For teams that embed the gate directly in their agent, the SDK is the same central gate as a native module. Enroll once; wrap dispatch around your tool-dispatch layer. The decision is made in-process (no network hop per call) and recorded in a post-quantum-signed audit chain that the control plane re-verifies.

Python (pip install niyam-sdk):

import niyam

gate = niyam.GovernedGate("http://control.internal:9090", "enrollment-token", "billing-agent")

decision = gate.dispatch(capability_token, "tools.email.send", "mailbox/support")
if decision.allowed:
    send_email()
else:
    log_refusal(decision.reason)

gate.flush_audit()                 # push the signed record centrally
assert gate.verify_audit()         # and it verifies locally too

Node (npm install niyam-sdk):

const { GovernedGate } = require('niyam-sdk')

const gate = GovernedGate.enroll('http://control.internal:9090', 'enrollment-token', 'billing-agent')

const decision = gate.dispatch(capabilityToken, 'tools.email.send', 'mailbox/support')
if (decision.allowed) sendEmail()

dispatch enforces the token's signature, its scope and resource, the central policy, and the revocation set, then records the decision. A central policy change or a token revocation reaches every gate on its refresh interval; the same call then denies. Both SDKs are verified end to end against a live control plane on every release.

Author policy centrally

Policy is authored once, centrally, and pulled by every node and SDK. An embedded gate can never weaken it.

# permit the support agent to send from one mailbox; everything else is deny by default
[[policy]]
name = "allow_support_email"
effect = "permit"
priority = 10
conditions = [ { action = "tools.email.*" } ]
niyam ctl --token $ADMIN policy set policy.toml     # raw policy
# or compile a single Lipi source to the policy plus an OPA Rego export and a review doc:
niyam ctl --token $ADMIN policy apply policy.lipi

Fine-grained, per-agent authority rides on capability tokens minted centrally:

niyam ctl --token $ADMIN agent create --name billing-agent
niyam ctl --token $ADMIN token mint \
  --subject did:key:z6Mk... \
  --scope 'tools.email.send=mailbox/support' \
  --ttl-seconds 3600
niyam ctl --token $ADMIN token revoke --id <token-id>   # denied everywhere within one interval

Prove everything: audit and compliance

Every decision, at the gateway or in the SDK, is appended to a hash-linked chain and signed ML-DSA-65 hybrid with Ed25519. The control plane holds each node's public bundle and re-verifies each chain it receives; a tampered or mis-signed segment is rejected and flagged, and the counter that tracks it is a first-class alert.

Pramana turns that record into auditor-ready evidence bundles mapped to ISO 42001, the EU AI Act, and NIST AI RMF, and Pariksha attaches signed model and agent evaluation evidence. Verification is offline: an auditor checks the signatures against the pinned issuer, without trusting the exporter.

What it runs like

Measured end to end on real cloud instances (see docs/CAPACITY.md for the method and the full report).

Path Measured
Policy checks (control plane reads) 58,000 to 77,000 per second on 4 cores, zero errors
Audit ingest (control plane writes, center-verified) ~2,000 to 2,900 per second (SQLite), ~1,100 per second (Postgres)
Control-plane memory ~20 MB and flat while the stored audit grows to gigabytes on disk
Gateway throughput (safety off) ~980 (2c) / 1,770 (4c) / 2,870 (8c) / 3,820 (16c) requests per second
Content safety on a GPU (NVIDIA A40) ~196 per second prompt-injection, ~100 per second output moderation
Content safety on CPU 17 to 44 per second injection, 1 to 4 per second output (move it to a GPU service)
Embedded SDK gate ~450 dispatch per second per vCPU, near-linear to ~4,500 per second at 8 cores

Footprint:

  • One static binary per OS and CPU. The control plane's resident memory stays bounded no matter how large the audit record grows: it keeps a small summary per node and reads chains from its store on demand.
  • SQLite default means no external service to run and a single file to back up.
  • Under failover, no audit records are lost: a data-plane node reconnects to the standby and resumes pushing from exactly where it stopped.

Enterprise operations

  • High availability. An active control plane plus a warm standby on shared Postgres; the standby reconciles its cache on a timer. Killing the active keeps every gateway enforcing on cached policy, and on failover each node reconnects and resumes its audit push from its cursor. Validated live, with no lost records.
  • Safe upgrades. The store carries a schema version and migrates forward on boot, and refuses to start against a store newer than the binary. The node-to-centre protocol is versioned, so an incompatible pair fails loudly instead of mis-governing.
  • Observability. A Prometheus endpoint on both planes exposes governance signals as first-class metrics: center-verify failures, dark nodes, safety fail-closed, store write errors, and capability rejections. Alert rules and a Grafana dashboard ship in deploy/observability/.
  • Pluggable store. SQLite (default) or Postgres behind one seam; the audit table is an append-only sink an OLAP system can read.
  • Post-quantum. ML-DSA-65 hybrid with Ed25519 on every signature; hybrid verifiers reject PQ-only payloads and vice versa.

Deployment profiles

The same binary serves every role. Pick a profile with --profile:

Profile What runs Serves
control policy, keys, capability issuer, audit aggregation, console 9090
gateway LLM gateway (Marg) plus Kavach gate plus PQ audit 8080
safety gateway plus Drishti content safety (embedded) 8080
full safety plus the Marg admin router 8080 and /admin
safety-server the content-safety models as a shared service (put it on a GPU) 8443
gate the embedded action gate, as the SDK, inside your own app in your app

Install

Every release ships one tested-together set of artifacts (niyam bom prints the exact pinned versions). See CHANGELOG.md for what is in each release.

  • Docker (the fastest way to run a node), from Docker Hub as sarthiai/niyam (CPU) and sarthiai/niyam:latest-gpu (the GPU safety-server). Run a control plane, with the console and admin API on :9090:
    docker run -p 9090:9090 sarthiai/niyam \
      run --profile control --config /etc/niyam/profiles/control.toml
    Change the admin and enrollment tokens in that config before real use, and pin a version tag (sarthiai/niyam:0.1.0) in production. Or docker compose up in deploy/ to bring up a control plane plus a governed node together. Models are not baked in; a safety node fetches and verifies them at run time.
  • The niyam binary. Download the archive for your OS and CPU from the Releases page, unpack it, and run ./niyam. Prebuilt for macOS (Apple silicon), Linux (x86_64 and aarch64), and Windows (x86_64). The archive includes the profile templates and provision-models.sh; the safety and full profiles need the ONNX runtime and models, which that script fetches (control and gateway need neither).
  • The SDK, to embed the gate in your own agent code:
    • Python: pip install niyam-sdk
    • Node: npm install niyam-sdk

To build from source instead, use the Quick start.

What is inside

Niyam assembles three independent libraries, consumed as pinned crates.io dependencies, plus five pieces built in this repository:

  • Kavach: the action gate and the post-quantum audit chain. The trust root.
  • Marg: the LLM traffic gateway. It embeds Kavach.
  • Drishti: content safety (prompt injection, PII and PHI, output safety).
  • niyam-types: the shared contract (audit chain format, principal identity, policy artifact).
  • Mudra: agent identity and post-quantum capability tokens, gated at the tool boundary.
  • Lipi: the policy compiler. One source, compiled and distributed centrally.
  • Pariksha: the evaluation harness. Signed model and agent evidence.
  • Pramana: the regulatory mapper. ISO 42001, EU AI Act, and NIST evidence bundles.

The Kavach line Niyam pins equals the Kavach line Marg embeds, so the trust root is a single copy across the workspace, checked by a coherence gate on every release.

Documentation

License

Elastic License 2.0 (LicenseRef-Elastic-2.0). See LICENSE. Self-hosted use; your data and audit never leave your infrastructure.


Designed, developed, and maintained by Chirotpal

Releases

Packages

Contributors

Languages