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 devgive 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 devruns your agent against an embedded loopback server with a seeded trust profile;--strictruns 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).