Get started

Quickstart

Connect an agent, create an app, attach a policy, and trigger your first authorized call — pick the path that fits your stack.

This is the ten-minute path from zero to "an agent called GitHub through Nomos and the receipt is in the audit chain." Four access modes are first-class — pick the one that matches how you'll actually use Nomos:

  • CLI@auto-nomos/cli. Best for scripting, CI, and one-off shell use.
  • MCP@auto-nomos/mcp-server. The path Claude Desktop, Cursor, and Claude Code take. Zero code on your side.
  • SDK · TS@auto-nomos/sdk. What your TypeScript / Node codebase calls when you're writing the agent yourself.
  • SDK · Pyauto-nomos-sdk on PyPI. Mirrors the TS surface for LangGraph / CrewAI / AutoGen orchestrators.

Pick a tab in any step below; your choice is remembered across steps.

Before you start

  • A Nomos account (sign up at the dashboard — takes 30 seconds).
  • An API key from /app/api-keys (NOMOS_API_KEY=nk_live_…). Visible once on creation.
  • Node 18+ if you're using the CLI, MCP server, or TypeScript SDK. Python 3.10+ for the Python SDK.

1. Connect an agent

Every agent identifies itself to Nomos with an API key. CLI users export it once. MCP clients put it in their config JSON. SDK users pass it to createIntentClient(). That key tells the control plane who is asking — it does not grant access on its own; policy still decides every call.

path
typescript
import { createIntentClient } from '@auto-nomos/sdk';

const client = createIntentClient({
  controlPlaneUrl: process.env.NOMOS_CONTROL_URL!,
  apiKey: process.env.NOMOS_API_KEY!,
});

2. Create an App

An App is one agent's identity inside your organization. It has a DID (used to sign UCANs), an optional Cedar policy, and zero or more API keys. The App name shows up in every audit row forever — name it accurately.

path
typescript
const app = await client.apps.create({
  name: 'Inbox triage bot',
  mode: 'dynamic',
});

const key = await client.apps.keys.create({ appId: app.id });
// store key.value somewhere — it's only returned once

Prefer the dashboard?

Every step here also works in the web UI. Apps → Create App issues the same App and key. The dashboard is the path used by your teammates who don't write code.

3. Attach a policy

Without a policy, every authorize denies — fail-closed by design. The fastest path is the starter template github:read-only, which lets the agent list issues, read repo metadata, and nothing else. Edit it later or build your own in the visual policy builder.

path
typescript
const policy = await client.policies.createFromTemplate({
  template: 'github:read-only',
  name: 'GitHub read',
});

await client.apps.attachPolicy({
  appId: app.id,
  policyId: policy.id,
});

The Cedar that template ships is short enough to read:

cedar
permit (
  principal,
  action == Action::"github:issue:list",
  resource is GithubRepo
)
when { principal.app == resource.allowed_app };

4. Trigger your first call

Now hit /v1/authorize, get a UCAN back, then use it against the PDP to actually call GitHub. The PDP holds the decrypted OAuth token — your agent never sees it.

path
typescript
const grant = await client.authorize({
  command: '/github/issue/list',
  resource: { provider: 'github', owner: 'acme', repo: 'app' },
  ttlSeconds: 300,
});

if (grant.decision !== 'allow') throw new Error(grant.reason);

const issues = await fetch(
  `${process.env.NOMOS_PDP_URL}/github/issue/list?owner=acme&repo=app`,
  { headers: { authorization: `Bearer ${grant.ucan}` } },
).then((r) => r.json());

Verify it worked

  • /app/audit shows two new rows: the authorize decision and the /github/issue/list proxy call.
  • The receipt drawer on the proxy row shows prevHash that matches the hash of the authorize row above it.
  • "Download proof" gives you a JSON bundle @auto-nomos/audit-verify can verify offline against your signed daily root.

You're live

You've shipped an end-to-end authorized call: agent identified, policy checked, action proxied, receipt recorded — without your code ever holding a GitHub token. Same shape works for every integration in the catalog.