Reconnecting
Restoring connection…
Reconnecting
Restoring connection…
Docs navigation
Get started
Connect an LLM
Operate
Build packs
Author your own pack
A pack is a directory of YAML you write and maintain yourself — no SDK, no compiler, no signing keys, nothing uploaded to anyone. Drop it on the runners you control, trust it once, and your model can call it. Most packs stay private to your fleet, and that's by design: the public registry is curated, not a free-for-all (more on that below).
$ emisar pack validate ./my-pack pack my-pack OK: 2 actions hash: sha256:e0487aa2762564261db5fc8e7efd5bfef895b33c8d9fa6342b1408cc602a97ab $ sudo emisar pack install ./my-pack installed my-pack → /etc/emisar/packs/my-pack my-pack — My ops pack (v0.1.0) Actions: 2 (2 low)
Use your coding agent
The author-pack
skill walks your agent through writing, validating, testing, and trusting a pack of your own.
Write an emisar action pack for the tool I name, validate and test it, and get it trusted on my runner. Use the author-pack skill: https://raw.githubusercontent.com/AndrewDryga/emisar/main/skills/author-pack/SKILL.md
Works in Claude Code, Codex, or any agent that reads Markdown skills — read the skill first .
-
1 Lay out the pack directory
A pack is just a folder: a top-level
pack.yamlmanifest and one YAML per action inactions/. Optionalscripts/holds packaged shell scripts forkind: scriptactions. Keep the whole thing in your own git repo.my-pack/ pack.yaml # pack manifest (see step 2) actions/ do_thing.yaml # one declared action per file do_other_thing.yaml scripts/ # only when you have script actions do_thing.sh -
2 Write pack.yaml
The manifest declares pack-level metadata and which action YAMLs to load.
idis the pack slug (used in the install path and, for self-maintained packs, however you reference it internally).yamlschema_version: 1 id: my-pack name: My ops pack version: 0.1.0 description: Short one-line summary shown on the runner + dashboard. vendor: acme homepage: https://github.acme.internal/sre/my-pack # your repo; optional requires: os: [linux] binaries: [my-cli] # optional — advertised to cloud; a missing one fails at run time actions: - actions/do_thing.yaml - actions/do_other_thing.yaml
-
3 Declare each action
One YAML per action — the runner refuses anything not in the catalog, so this file is the contract between your ops team and the LLM. This is where the safety lives: tight argument validation, an honest
side_effectslist, and the rightrisktier. See the YAML reference for the action fields.yamlschema_version: 1 id: my.do_thing title: Do the thing kind: exec risk: low description: > One-paragraph human description. Shown verbatim to the LLM — write it like a doc string, not a comment. side_effects: - Reads /var/log/myapp/*. - Touches nothing. args: - name: file type: path required: true validation: allowed_prefixes: ["/var/log/myapp/"] execution: command: binary: cat # bare name — resolved via PATH on the host argv: ["{{ args.file }}"] timeout: 10s output: parser: text max_stdout_bytes: 65536 max_stderr_bytes: 8192 # Optional. Renders on the dispatch form so operators (and LLMs) # see a real invocation before they fill in args. examples: - title: Tail myapp's error log args: file: /var/log/myapp/error.log -
4 Validate, install, and trust it
Validate locally first — the same checks the runner runs at load — then install it. Install writes to the runner's configured packs dir and reloads the running daemon itself, so there's no restart and no dropped runs:
shell# on the runner host emisar pack validate ./my-pack sudo emisar pack install ./my-pack
Because your pack isn't one emisar publishes, it has no baseline hash — so it lands in the dashboard's Packs page as pending, with dispatch held until you approve it. Open Packs, review the content hash, and click Trust. Its actions are then callable, gated by your policy. From then on, that exact byte-for-byte version is the only one authorized: edit the pack and its hash changes, which re-marks it pending until you re-trust. That's your drift guard — trust in bytes you reviewed, not a publisher you hope is honest.
Confirm it end-to-end: open the runner in the dashboard, check your actions under Advertised actions, and Run one.
-
5 Roll it out and maintain it
To put the pack on another runner, install it pinned to the hash you trusted, so every host runs identical bytes — the install reloads the runner for you. (Config management like Ansible, Chef, or a base image can drop the directory itself instead, then reload the runner.)
shellsudo emisar pack install ./my-pack --hash sha256:<the hash you trusted>
When you change the pack, bump its
version, re-validate, re-install (add--force— an already-installed pack isn't replaced without it), and re-trust the new hash once in the dashboard — the same review you'd give any production change. The pack is yours: its lifecycle, its hosts, its risk tiers, on your schedule.Distributing many packs to a large fleet? Skip the copying and host your own pack registry.
Keep it yours — or propose it to the registry#
The public registry behind the Packs page is curated by us, deliberately. We only take packs that are genuinely generic and broadly useful — Postgres, Cassandra, Docker, Linux core, the AWS suite — the kind of thing many teams run unchanged. We don't accept every submission, because an open dumping ground of one-off, environment-specific packs would be a mess: hard to trust, hard to maintain, and low signal for everyone browsing it.
Most packs should stay yours. A pack that hardcodes your service names, your hostnames, or your internal CLIs is more useful private — and you keep control of its lifecycle. Self-maintained is the default here, not a fallback.
And keeping a pack private still scales to a whole fleet — no copying files host to host. packctl, the same tool that builds
emisar's own registry, publishes yours from a
packs/
directory to any bucket or web server you control, and every runner installs from it with emisar pack install <pack> --registry <your-url>.
Same hash-pinned review-and-trust flow as the public registry — your packs never touch ours.
Host your own pack registry
walks it end to end.
If your pack really is generic — useful to teams who've never heard of yours, with no
environment-specific assumptions — we'd love to ship it. Open a PR against the
repo
adding it under packs/
— the catalog is built from the tree, so there's no registry-code entry to edit — or email
support@emisar.dev
first if you're not sure it clears the bar. We review for:
- Genuinely generic — no hardcoded hosts, service names, or org-specific paths. If it only makes sense inside your company, it belongs in your repo, not ours.
-
Tight argument validation
— no free-form shell strings; paths use
allowed_prefixes, choice args usevalidation.enum, and numbers use min/max. -
Honest
side_effects— every file, network, and process side effect listed. The LLM reads these. -
Correct
risklevel — destructive actions (restart, delete, repair) arehighorcritical; reads arelow. -
Resolvable binaries
— declare what the runner needs on the host under
requires.binaries.