Closes the gap between "the endpoints exist" and "a runner can actually run a job." Pairs with the werkbank-repo change (runner auth + blob fetch).
New control-plane endpoints (runner-token gated)
POST /api/v1/werkbank/jobs/enqueue{tenant, target_id} — the enqueue half of the loop: extract the target's control-logic program (same selection as the in-process PLC scan), stash the source as a content-addressed blob, and queue a plc-provision job referencing it by hash. Returns {job_id, enqueued}.
GET /api/v1/werkbank/artifacts/{hash} — serve a blob so the runner can fetch the program. Traversal-safe (the hash is validated to 64 hex chars).
Supporting
ingest::blob: store_bytes / read_blob helpers; the module is now pub(crate).
.env.example documents WERKBANK_RUNNER_TOKEN.
The loop, end to end
With the werkbank-side auth + blob fetch:
enqueue → runner leases → Docker executor fetches the program by hash → provisions
OpenPLC, loads it, probes Modbus + DASTs → completes → findings persisted to target
Tests
New integration test enqueues from a PlcSps target, confirms the job carries a program blob, and serves it back. All 4 werkbank_api tests pass; clippy + fmt clean.
Note
This adds a manual/testable enqueue trigger; auto-enqueue-on-scan (orchestrator cut-over) can follow. Blob programs now work end-to-end; on-prem runners will resolve blobs from a local store instead of the control plane.
Closes the gap between "the endpoints exist" and "a runner can actually run a job." Pairs with the werkbank-repo change (runner auth + blob fetch).
## New control-plane endpoints (runner-token gated)
- **`POST /api/v1/werkbank/jobs/enqueue`** `{tenant, target_id}` — the **enqueue** half of the loop: extract the target's control-logic program (same selection as the in-process PLC scan), stash the source as a **content-addressed blob**, and queue a `plc-provision` job referencing it by hash. Returns `{job_id, enqueued}`.
- **`GET /api/v1/werkbank/artifacts/{hash}`** — serve a blob so the runner can fetch the program. Traversal-safe (the hash is validated to 64 hex chars).
## Supporting
- `ingest::blob`: `store_bytes` / `read_blob` helpers; the module is now `pub(crate)`.
- `.env.example` documents `WERKBANK_RUNNER_TOKEN`.
## The loop, end to end
With the werkbank-side auth + blob fetch:
```
enqueue → runner leases → Docker executor fetches the program by hash → provisions
OpenPLC, loads it, probes Modbus + DASTs → completes → findings persisted to target
```
## Tests
New integration test enqueues from a `PlcSps` target, confirms the job carries a program blob, and serves it back. All 4 `werkbank_api` tests pass; clippy + fmt clean.
## Note
This adds a manual/testable enqueue trigger; auto-enqueue-on-scan (orchestrator cut-over) can follow. Blob programs now work end-to-end; on-prem runners will resolve blobs from a local store instead of the control plane.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes the gap between "endpoints exist" and "a runner can actually run a job":
- POST /api/v1/werkbank/jobs/enqueue {tenant, target_id} — the control-plane
"enqueue" half: extract the target's control-logic program, stash the source as
a content-addressed blob, and queue a plc-provision job referencing it by hash.
- GET /api/v1/werkbank/artifacts/{hash} — serve a blob so the runner can fetch
the program (traversal-safe: the hash is validated). Both runner-token gated.
- blob.rs: store_bytes / read_blob helpers; ingest::blob is now pub(crate).
- .env.example documents WERKBANK_RUNNER_TOKEN.
With the runner-side auth + blob fetch (werkbank repo), the loop runs end to end:
enqueue -> lease -> Docker executor fetches the program by hash, provisions,
probes, DASTs -> completes -> findings persisted against the target.
Tests: a new integration test enqueues from a PlcSps target, confirms the job
carries a program blob, and serves it back. All 4 werkbank_api tests pass; clippy
+ fmt clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sharang
merged commit 94c0d51a11 into main2026-07-17 14:34:01 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes the gap between "the endpoints exist" and "a runner can actually run a job." Pairs with the werkbank-repo change (runner auth + blob fetch).
New control-plane endpoints (runner-token gated)
POST /api/v1/werkbank/jobs/enqueue{tenant, target_id}— the enqueue half of the loop: extract the target's control-logic program (same selection as the in-process PLC scan), stash the source as a content-addressed blob, and queue aplc-provisionjob referencing it by hash. Returns{job_id, enqueued}.GET /api/v1/werkbank/artifacts/{hash}— serve a blob so the runner can fetch the program. Traversal-safe (the hash is validated to 64 hex chars).Supporting
ingest::blob:store_bytes/read_blobhelpers; the module is nowpub(crate)..env.exampledocumentsWERKBANK_RUNNER_TOKEN.The loop, end to end
With the werkbank-side auth + blob fetch:
Tests
New integration test enqueues from a
PlcSpstarget, confirms the job carries a program blob, and serves it back. All 4werkbank_apitests pass; clippy + fmt clean.Note
This adds a manual/testable enqueue trigger; auto-enqueue-on-scan (orchestrator cut-over) can follow. Blob programs now work end-to-end; on-prem runners will resolve blobs from a local store instead of the control plane.
🤖 Generated with Claude Code
Closes the gap between "endpoints exist" and "a runner can actually run a job": - POST /api/v1/werkbank/jobs/enqueue {tenant, target_id} — the control-plane "enqueue" half: extract the target's control-logic program, stash the source as a content-addressed blob, and queue a plc-provision job referencing it by hash. - GET /api/v1/werkbank/artifacts/{hash} — serve a blob so the runner can fetch the program (traversal-safe: the hash is validated). Both runner-token gated. - blob.rs: store_bytes / read_blob helpers; ingest::blob is now pub(crate). - .env.example documents WERKBANK_RUNNER_TOKEN. With the runner-side auth + blob fetch (werkbank repo), the loop runs end to end: enqueue -> lease -> Docker executor fetches the program by hash, provisions, probes, DASTs -> completes -> findings persisted against the target. Tests: a new integration test enqueues from a PlcSps target, confirms the job carries a program blob, and serves it back. All 4 werkbank_api tests pass; clippy + fmt clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>