Dev Platform
@omadia/dev-platform
Runs autonomous coding jobs against your GitHub repositories. An agent clones a repo into an isolated runner, drives an LLM through an analyze/plan/implement/review pipeline, and opens a pull request — bounded at every step by human approval gates, a diff policy, a per-job cost budget and an egress allowlist. Ships its own operator UI, HTTP routes, chat tools, background workers and nine database tables it migrates itself. Runners execute on Fly Machines or on local Docker; neither ever sees a long-lived credential. Extracted from omadia core into its own installable plugin (epic byte5ai/omadia#470).
- latest
- v0.3.5
- license
- MIT
- versions
- 5
- author
- byte5 GmbH
install
In your omadia instance, open Admin → Registries and add this registry, then install Dev Platform from Admin → Plugins → Store.
https://hub.omadia.aisetup guide
Dev Platform
Runs coding jobs against your repositories: an agent clones the repo in an isolated runner, works the job, and opens a pull request. Human gates, diff policy and per-job LLM budgets bound what it can do.
Before you install
omadia core 1.5 or newer. The plugin activates against extension
points that landed across five core releases; the newest is
ctx.sql.seedLedger (the migration handoff). On an older core the handoff
silently does not happen and the migrations re-run against a schema core
already created.
A Postgres-backed knowledge graph is required. The job spine, repo,
gate and artifact tables live there. With an in-memory graph the plugin
refuses to activate rather than pretending to work — set DATABASE_URL
and install the Neon knowledge-graph plugin first.
Two operator grants — the next step after this form
This plugin declares two permissions that a manifest alone cannot give it. A human has to agree, separately, and neither is implied by installing.
You are asked on the next step. Once this form is accepted the wizard shows Permissions, listing exactly what is below and nothing else. Grant & activate records your consent and activates the plugin in the same request. Skip grants nothing and does not lose the install — the same choices live on the plugin page afterwards, under Permissions, with a toggle per grant. Either way the plugin is re-activated in place: no middleware restart.
1. The SQL permission (permissions.sql). The plugin owns nine tables
and migrates them itself into its own ledger,
plg_omadia_dev_platform_migrations. It cannot read or write omadia's
tables or another plugin's. The ledger name comes from this manifest and
the granting identity from your session — neither can be supplied by a
request.
This plugin reaches for the database in
activate(), so skipping this grant leaves iterroredrather than half-working, and the wizard says so instead of guessing. Granting it later from the plugin page brings it up; no reinstall.
2. The public-path grants (permissions.public_paths). Three URL
prefixes are served without a kernel session, because the caller has no
session to present. Each is still authenticated, just not by one:
/api/v1/dev-runner— a per-job bearer token, verified in the router./api/webhooks/github— HMAC-SHA256 over the raw request body./api/v1/dev-platform— the GitHub App manifest-conversion callback, bound to a kernel-signed, plugin-audience state token.
Declining one is not an activation failure: the prefix simply stays behind the login and answers 401. Without them the runner cannot phone home, webhooks 401, and GitHub App onboarding cannot complete.
Consent can never exceed the declaration — core rejects anything this manifest does not ask for, so the consent surface cannot itself be used to hand a plugin your database or make an arbitrary URL public.
For automation, one route carries both, behind operator auth:
GET /api/v1/admin/runtime/installed/@omadia%2Fdev-platform/grants
PUT /api/v1/admin/runtime/installed/@omadia%2Fdev-platform/grants
{ "sql": true,
"public_paths": ["/api/v1/dev-runner",
"/api/webhooks/github",
"/api/v1/dev-platform"] }
An absent key leaves that grant alone; a present public_paths is the
COMPLETE set, so omitting a prefix revokes it. The older
…/public-paths route still works unchanged.
On a core older than byte5ai/omadia#824 none of the above exists:
GET …/grantsanswers 404, the SQL grant is a hand-written row inplugin_sql_grantsfollowed by a middleware restart, and only the…/public-pathsroute is available. The plugin's OPERATOR-GUIDE Appendix A has that procedure in full.
What this plugin does without, and what you lose
Four capabilities are declared optional. The plugin installs and runs when core does not provide them, and each absence is logged rather than silent. This table is the only place you can read it before installing — the registry does not carry optional capabilities.
| Capability | Absent means |
|---|---|
turnContext@1 | The chat dev-job tools are not registered at all, rather than registered and always refusing. They authorize per call against the human driving the turn; with no envelope there is nothing to authorize against. Use the operator UI instead. |
githubAppJwt@1 | The plugin falls back to a local RS256 signer. Functionally equivalent; the App private key is held by the plugin rather than by core. |
usageTelemetry@1 | No rows in the operator cost dashboard's ledger. Per-job budgets are unaffected — they meter this plugin's own tables and keep enforcing. |
conductorRoles@1 | Repositories whose approver is a ROLE rather than a person cannot have their gates approved. Fail-CLOSED — the safe direction, but a regression you should know about. Assign named approvers instead. |
Only graphPool@1 is mandatory. If core cannot provide it the install is
refused with install.missing_capability (HTTP 409).
Where your runners execute
Two backends, chosen by what you configure in the deployment — not here:
- Fly Machines — one ephemeral Machine per job, in a dedicated Fly
app named by
DEV_FLY_RUNNER_APP. This is the production path. The app must exist before the first job. - Local Docker — the compose overlay shipped with this repository
(
docker-compose.dev-platform.yaml), which brings up the runner daemon, the egress proxy and one privileged docker-in-docker service. Best for evaluation and self-hosting.
Either way the runner reaches this middleware at the phone-home URL below, so a loopback address only works when the runner shares the host.
What lives here, and what lives in your deployment
The fields below are policy — the decisions only you can make: how much a job may spend, how long it may run, how many run at once, which models it may call, how long its history is kept.
Infrastructure is not asked for here. The runner image, the runner
daemon URL and token, and the dedicated Fly app are set in the compose
overlay alongside the sidecars themselves (DEV_RUNNER_IMAGE,
DEV_RUNNER_DAEMON_URL, DEV_RUNNER_DAEMON_TOKEN, DEV_FLY_RUNNER_APP).
A second copy in this form would be a second thing to keep in sync.
Two switches that need a second confirmation
Subscription mode runs jobs against a CLI subscription credential inside the runner. Unsafe local backend runs the shim as a local process instead of in a container. Each demands its paired acknowledgment field, and activation is refused if you turn one on without it. That refusal is deliberate: both modes put a credential or your host filesystem within reach of code an agent wrote.
Upgrading from the Dev Platform that ran inside core
Migration slots 0022–0030 are already applied on your database and recorded in CORE's ledger. This plugin adopts them instead of re-applying them — but only where a WITNESS proves the schema object each file creates is actually present. The case that makes this necessary is rows present, tables absent: a restore from an older snapshot, a version-skewed rollback, a table dropped during an incident.
handoff-plan.json ships inside the ZIP so you can see the plan against
your real database BEFORE installing. Dry run is the default:
cd <omadia>/middleware && npm run build
DATABASE_URL=… node scripts/plugin-ledger-handoff.mjs \
--plan <zip>/handoff-plan.json
Read skippedNoWitness in the output. Empty is healthy. Non-empty means
core's ledger and your live schema disagree — not a failure of the
handoff, but a reason to confirm the database is the one you think it is.
After installing — you re-enter the credentials
Connect a repository under Dev Platform → Repositories. A GitHub App is the recommended credential — the runner then receives a freshly minted, single-repo, read-only token that is revoked when the job ends.
GitHub App keys and repository tokens are not migrated for you. This
plugin stores credentials in its own vault namespace, so App signing
material, webhook secrets and per-repo clone tokens are entered here once,
by you.
That is a deliberate choice over a one-time migration hook: the right
moment to see that a GitHub App private key has moved into a plugin's
namespace is while it is happening. The same applies after a reinstall.
See docs/SECRETS.md for the full list and for rotation.
versions
setup fields
Values the operator fills in at install-time.
permissions
sql
migrations: migrations · ledger: plg_omadia_dev_platform_migrations · handoff: handoff-plan.json
public_paths
0: /api/v1/dev-runner · 1: /api/webhooks/github · 2: /api/v1/dev-platform
secrets
runtime_write: true
network
outbound: [4]