The control-plane side of the pull API — implements sharang/werkbank#6. A Werkbank runner can now reach a real queue end-to-end.
Endpoints (/api/v1/werkbank/jobs/*)
Thin handlers over the WB-02 JobQueue, tenant-scoped from the request's tenant:
POST …/lease — lease the oldest runnable job (200 + LeasedJob) or 204 when empty.
POST …/heartbeat — extend the lease (200 + HeartbeatAck), or 409 when the lease is lost.
POST …/complete — record the terminal result, then persist the runner's findings + DAST findings against the job's target (dedup'd by fingerprint) — so a job run by a remote runner lands the same findings an in-process run would.
Auth
A static WERKBANK_RUNNER_TOKEN bearer (require_runner_token), mounted only when the token is set — mirroring the admin API, and deliberately not a Keycloak JWT (a runner acts across tenants). /api/v1/werkbank/* is added to the JWT PUBLIC_PREFIXES so it routes to the runner-token gate rather than the customer-JWT path.
Shared contract
LeaseRequest / HeartbeatRequest / CompleteRequest / CompleteResponse live in compliance-core::models::werkbank, so the runner (client, in the werkbank repo) and the control plane (server) compile against one set of types.
Tests
3 HTTP integration tests against a live Mongo — lease→complete→persist, empty-queue 204, and the bearer-token gate (missing/wrong → 401) — plus a token-compare unit test. Skip cleanly with no Mongo. clippy + fmt clean.
Follow-up
Wiring the scan pipeline to enqueueplc-provision jobs needs an artifact-fetch path so the runner can pull the program blob — tracked with the on-prem work (werkbank#8). The werkbank runner will switch from its local transport structs to these shared compliance-core types once this merges to main.
The control-plane side of the pull API — implements sharang/werkbank#6. A Werkbank runner can now reach a real queue end-to-end.
## Endpoints (`/api/v1/werkbank/jobs/*`)
Thin handlers over the WB-02 `JobQueue`, tenant-scoped from the request's `tenant`:
- **`POST …/lease`** — lease the oldest runnable job (`200` + `LeasedJob`) or `204` when empty.
- **`POST …/heartbeat`** — extend the lease (`200` + `HeartbeatAck`), or `409` when the lease is lost.
- **`POST …/complete`** — record the terminal result, then **persist** the runner's findings + DAST findings against the job's target (dedup'd by fingerprint) — so a job run by a remote runner lands the same findings an in-process run would.
## Auth
A static **`WERKBANK_RUNNER_TOKEN`** bearer (`require_runner_token`), mounted only when the token is set — mirroring the admin API, and deliberately **not** a Keycloak JWT (a runner acts across tenants). `/api/v1/werkbank/*` is added to the JWT `PUBLIC_PREFIXES` so it routes to the runner-token gate rather than the customer-JWT path.
## Shared contract
`LeaseRequest` / `HeartbeatRequest` / `CompleteRequest` / `CompleteResponse` live in `compliance-core::models::werkbank`, so the runner (client, in the werkbank repo) and the control plane (server) compile against one set of types.
## Tests
3 HTTP integration tests against a live Mongo — lease→complete→persist, empty-queue `204`, and the bearer-token gate (missing/wrong → `401`) — plus a token-compare unit test. Skip cleanly with no Mongo. clippy + fmt clean.
## Follow-up
Wiring the scan pipeline to *enqueue* `plc-provision` jobs needs an artifact-fetch path so the runner can pull the program blob — tracked with the on-prem work (`werkbank#8`). The werkbank runner will switch from its local transport structs to these shared `compliance-core` types once this merges to `main`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The control-plane side of the pull API (implements sharang/werkbank#6), so a
Werkbank runner can reach a real queue end-to-end:
- POST /api/v1/werkbank/jobs/{lease,heartbeat,complete} — thin handlers over the
JobQueue (WB-02), tenant-scoped from the request's `tenant` (db_pool
.for_tenant_id). lease→204 when empty; heartbeat→409 on a lost lease.
- Machine auth: a static WERKBANK_RUNNER_TOKEN bearer (require_runner_token),
mounted only when the token is set — like the admin API, and NOT a Keycloak JWT
(a runner acts across tenants). /api/v1/werkbank/* is added to the JWT
PUBLIC_PREFIXES so it routes to the runner-token gate, not the customer-JWT one.
- On completion, the runner's findings + DAST findings are persisted against the
job's target (dedup'd by fingerprint), so a job run by a remote runner lands
the same findings an in-process run would.
- Shared transport types (LeaseRequest/HeartbeatRequest/CompleteRequest/
CompleteResponse) live in compliance-core so the runner (client) and control
plane (server) agree on shapes.
Tests: 3 HTTP integration tests against a live Mongo (lease→complete→persist,
empty-queue 204, and the bearer-token gate) + a token-compare unit test. Skips
cleanly with no Mongo. clippy + fmt clean.
Follow-up: wiring the scan pipeline to enqueue plc-provision jobs needs an
artifact-fetch path for the runner (so it can pull the program blob); tracked
with the on-prem work.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sharang
merged commit 91a87677bc into main2026-07-17 11:42:30 +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.
The control-plane side of the pull API — implements sharang/werkbank#6. A Werkbank runner can now reach a real queue end-to-end.
Endpoints (
/api/v1/werkbank/jobs/*)Thin handlers over the WB-02
JobQueue, tenant-scoped from the request'stenant:POST …/lease— lease the oldest runnable job (200+LeasedJob) or204when empty.POST …/heartbeat— extend the lease (200+HeartbeatAck), or409when the lease is lost.POST …/complete— record the terminal result, then persist the runner's findings + DAST findings against the job's target (dedup'd by fingerprint) — so a job run by a remote runner lands the same findings an in-process run would.Auth
A static
WERKBANK_RUNNER_TOKENbearer (require_runner_token), mounted only when the token is set — mirroring the admin API, and deliberately not a Keycloak JWT (a runner acts across tenants)./api/v1/werkbank/*is added to the JWTPUBLIC_PREFIXESso it routes to the runner-token gate rather than the customer-JWT path.Shared contract
LeaseRequest/HeartbeatRequest/CompleteRequest/CompleteResponselive incompliance-core::models::werkbank, so the runner (client, in the werkbank repo) and the control plane (server) compile against one set of types.Tests
3 HTTP integration tests against a live Mongo — lease→complete→persist, empty-queue
204, and the bearer-token gate (missing/wrong →401) — plus a token-compare unit test. Skip cleanly with no Mongo. clippy + fmt clean.Follow-up
Wiring the scan pipeline to enqueue
plc-provisionjobs needs an artifact-fetch path so the runner can pull the program blob — tracked with the on-prem work (werkbank#8). The werkbank runner will switch from its local transport structs to these sharedcompliance-coretypes once this merges tomain.🤖 Generated with Claude Code