Skip to main content
Version: v5.7.0

12.7 Build one: the SDK and agent dev

packs:agents

After this you can: build an agent of your own with the SDK, run it against a server with opschain agent dev, and package it as a signed release.

Before you start — tick each one:

  • Lesson 12.2 — Enrol, own, assign, grant, ceiling (opschain learn 12.2)

In the GUI: nothing extra — agent dev runs its own loopback server; once your release is enrolled on a real server, the agent's sheet names it like any other (lesson 12.4).

So far you have operated Felix. This lesson builds it: a handful of lines against the OpsChain Agent SDK, the one-command development loop that runs them against an embedded server with no ceremony, and the packaging that turns the build into a signed release lesson 12.4's ladder can promote. Nothing here needs a licence — the local loop is deliberately waived from the entitlement gates; the production ceremony at the end does.

1. The SDK​

The OpsChain Agent SDK ships in two languages: Go (sdk/agent, the library the opschain agent runtime is built on) and Python (sdk/python/opschain_agent.py, one module with no dependencies beyond the standard library). Both give you the same things for free: enrolment with a registration token, a durable Ed25519 identity under a state directory, the outbound-only channel with reconnect and re-register, the five mandatory surfaces (healthz, readyz, info, metrics, logs), the request lane for calling the platform API, and one correlation scope per unit of work threaded through every call you make. You write your capabilities. The platform-side contract both SDKs implement is opschain docs agent-plane; the AI lane's adapters are opschain docs ai-agents.

The smallest Felix, in Python:

from opschain_agent import Agent, Response

agent = Agent(
"https://opschain.example.com",
token="<registration-token>", # first run only; the identity persists
name="felix",
state_dir="./felix",
)

@agent.chat
def chat(message):
yield "felix here — you said: " + message

@agent.capability("x-patch-plan")
def patch_plan(request):
return Response(200, {"Content-Type": "application/json"}, b'{"hosts": 12, "pending": 3}')

agent.run()

Two rules the server enforces on whatever you write: a capability you did not declare never reaches the agent (404 at the platform edge), and a change or workflow run the agent creates through the request lane (agent.call_api(...)) MUST carry a rationale attribute — the server refuses an agent-originated create without one, and the approver reads it back in the inbox (lesson 12.3).

2. Run it with agent dev​

One foreground command: it starts an embedded server on loopback, mints an identity, manifest and token for your agent (exactly the objects production holds), enrols it into a sandbox project, supervises your process and serves the agent tree on the loopback API:

opschain agent dev --project limebank --code felix --name felix --type service \
--purpose "BAU patching for limebank" -- python3 felix.py

On start it prints the dev server URL and home, the generated admin token (default profile), the agent's surface URLs (…/{healthz,readyz,info,metrics,logs,…}) and Ctrl-C stops the loop (the agent says goodbye first). With a trailing -- <command> the process is supervised and restarted on exit; --target http://127.0.0.1:9000 fronts a process you already run instead. Either way the chat capability is declared by default.

The default profile also seeds a trust profile — an assets-under- management assignment plus an AUTONOMOUS grant over the sandbox project — so your handler can request work on the first run. Edit it with the same verbs production uses, against the dev server:

opschain admin trust list --server http://127.0.0.1:7726 --auth-token <admin-token>

Then hit your surfaces with any HTTP client at the printed URLs and read the logs panel back the same way. The listen address is fixed (127.0.0.1:7726) so the agent's saved identity stays valid across runs of the same --dev-home.

The AI job description is the same loop with the disclosures the ai type must attest:

opschain agent dev --type ai --model anthropic/claude-sonnet-4-5 \
--adapter opschain-claude-adapter --tool-surface request_lane,docs \
--custody brokered -- python3 felix_ai.py

Locally no entitlement applies; on a licensed server the same manifest needs packs:agent-governance + packs:ai-agents (lesson 12.6).

3. The production ceremony, locally​

--strict seeds nothing: the registration-token ceremony and token-gated telemetry run exactly as production enforces them — the enforcement code is identical in both profiles, they differ only in which rows exist at the start:

opschain agent dev --strict --admin-token <token>

With no --token the command prints the exact opschain admin agent-token create invocation to mint one against the dev server and keeps the server running so you can; restart with --token <registration-token> and your agent enrols the way Felix did in lesson 12.2.

4. Package it as a signed release​

A build becomes something the estate can trust by becoming a release: a <name>@<version>, the manifest document, the content-addressed artifact (the image digest or git sha you built) and a signature from a registered key. Register the key once, sign in the pipeline, register the release:

opschain admin agent-release keygen --name release-pipeline --out ./felix-signing.key
opschain admin agent-release sign --name felix --version 2.2.0 \
--artifact sha256:4b7e… --manifest felix.json \
--key-file ./felix-signing.key --out felix-2.2.0.sig
opschain admin agent-release create --name felix --version 2.2.0 \
--artifact sha256:4b7e… --manifest felix.json --type service \
--key-id <key-id> --signature-file felix-2.2.0.sig
signed felix@2.2.0 (artifact sha256:4b7e…) with key fingerprint SHA256:…
signature written to felix-2.2.0.sig
created agent release felix@2.2.0 (id 019a0e11-…)
artifact: sha256:4b7e…
signed by: SHA256:…
stage: dev

sign makes no server call, so a pipeline signs offline; create re-verifies the signature server-side before the row lands and freezes the declared type (and any disclosures object in the manifest) into the signed content. The manifest file is the job description from lesson 12.0 as JSON — name, version, type, purpose, capabilities, disclosures. From here the release is at dev, and lesson 12.4's walk takes it up the ladder: promote, try it in the shadow lane, promote into prod under approval, roll the stable lane, retire the old one.

Running the real thing from that release is the production runtime with the release named:

opschain agent --server https://opschain.example.com --token <token> --code felix \
--release felix@2.2.0 --lane stable --dir ./felix

An SDK agent names it the same way (the Python release="felix@2.2.0" option, the Go ReleaseName/ReleaseVersion options, or OPSCHAIN_AGENT_RELEASE in the container's environment) and handles the platform-instructed restart of a rollover itself.

Check yourself​

  • What does agent dev give you that a production instance does not, and what signs a release?

What you learned​

  • The OpsChain Agent SDK (Go or Python) gives you identity, channel, the five surfaces, the request lane and correlation for free; you write capabilities.
  • opschain agent dev runs your agent against an embedded loopback server with a seeded trust profile; --strict runs the production ceremony locally.
  • A release is name, version, manifest and artifact under an Ed25519 signature — signed offline with a registered key, verified server-side, registered at dev.
  • From dev, lesson 12.4's ladder and rollover take your build into production.

Next: lesson 12.8 — The bundled SMEs (opschain learn 12.8), or back to your track (opschain learn).