@defrex/autobuild 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/SPEC.md +222 -22
- package/docs/architecture.md +66 -4
- package/docs/configuration.md +105 -16
- package/docs/hosted-dispatcher.md +17 -2
- package/docs/hosted-service.md +214 -0
- package/docs/mcp.md +8 -2
- package/docs/operator-api.md +90 -7
- package/docs/remote-store-protocol.md +189 -144
- package/docs/remote-ticket-protocol.md +107 -19
- package/docs/setup.md +173 -16
- package/docs/ticket-dependencies.md +1 -1
- package/package.json +7 -6
- package/packages/core/src/cli/attention.ts +209 -0
- package/packages/core/src/cli/configured-store.ts +17 -0
- package/packages/core/src/cli/connect.ts +261 -0
- package/packages/core/src/cli/dashboard/model.ts +4 -5
- package/packages/core/src/cli/dispatch.ts +183 -41
- package/packages/core/src/cli/help.ts +39 -10
- package/packages/core/src/cli/init-validation.ts +27 -2
- package/packages/core/src/cli/main.ts +41 -15
- package/packages/core/src/cli/mcp.ts +7 -1
- package/packages/core/src/cli/pr-summary-publication.ts +220 -0
- package/packages/core/src/cli/pr-summary.ts +2 -0
- package/packages/core/src/cli/project-credentials.ts +132 -0
- package/packages/core/src/cli/repo-state.ts +15 -2
- package/packages/core/src/cli/self-update.ts +10 -0
- package/packages/core/src/cli/store-opening.ts +22 -14
- package/packages/core/src/cli/terminals.ts +0 -10
- package/packages/core/src/cli/ticket.ts +8 -1
- package/packages/core/src/cli/upgrade-compatibility.ts +80 -0
- package/packages/core/src/cli/wait.ts +84 -17
- package/packages/core/src/cli/watch.ts +197 -60
- package/packages/core/src/config/live.ts +3 -0
- package/packages/core/src/config/schema.ts +56 -9
- package/packages/core/src/connection.ts +86 -0
- package/packages/core/src/events/catalog.ts +10 -2
- package/packages/core/src/events/payloads.ts +44 -0
- package/packages/core/src/events/repository.ts +13 -0
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +130 -83
- package/packages/core/src/kernel/engine.ts +24 -0
- package/packages/core/src/kernel/generators/build-log.ts +10 -0
- package/packages/core/src/kernel/log-index.ts +17 -0
- package/packages/core/src/kernel/pr-summary.ts +89 -0
- package/packages/core/src/kernel/reducer-registry.ts +8 -0
- package/packages/core/src/kernel/reducer.ts +8 -0
- package/packages/core/src/ontology.ts +1 -0
- package/packages/core/src/operator/index.ts +7 -0
- package/packages/core/src/operator/query.ts +38 -16
- package/packages/core/src/operator/ticket-source-backend.ts +1 -0
- package/packages/core/src/operator/tickets.ts +6 -8
- package/packages/core/src/plugin-sdk/index.ts +1 -0
- package/packages/core/src/plugins/manifest.ts +5 -3
- package/packages/core/src/plugins/runtimes.ts +3 -1
- package/packages/core/src/ports/forge/fake.ts +13 -1
- package/packages/core/src/ports/forge/github-transport.ts +36 -1
- package/packages/core/src/ports/forge/github.ts +29 -5
- package/packages/core/src/ports/forge/local-git.ts +12 -4
- package/packages/core/src/ports/forge/summary-marker.ts +8 -0
- package/packages/core/src/ports/runner/session-env.ts +1 -0
- package/packages/core/src/ports/tickets/create.ts +11 -3
- package/packages/core/src/ports/tickets/hosted-index.ts +0 -3
- package/packages/core/src/ports/tickets/remote-protocol.ts +1 -0
- package/packages/core/src/ports/tickets/remote.ts +1 -0
- package/packages/core/src/ports/workspace/build-execution.ts +5 -1
- package/packages/core/src/ports/workspace/create.ts +11 -2
- package/packages/core/src/ports/workspace/distribution-archive.ts +3 -5
- package/packages/core/src/ports/workspace/harvest-execution.ts +3 -1
- package/packages/core/src/ports/workspace/local-build-execution.ts +8 -1
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +47 -19
- package/packages/core/src/processes/build-execution-state.ts +1 -1
- package/packages/core/src/processes/build-runner.ts +87 -26
- package/packages/core/src/processes/dispatch-failure-budget.ts +83 -0
- package/packages/core/src/processes/dispatcher.ts +59 -10
- package/packages/core/src/processes/harvest-child.ts +1 -0
- package/packages/core/src/processes/harvest-execution-state.ts +19 -2
- package/packages/core/src/processes/harvest-runner.ts +142 -100
- package/packages/core/src/processes/phase-failure-budget.ts +1 -0
- package/packages/core/src/processes/publication-loss.ts +18 -1
- package/packages/core/src/store/remote/client.ts +1 -1
- package/packages/core/src/store/remote/compatibility.ts +90 -0
- package/packages/core/src/store/remote/credentials.ts +28 -0
- package/packages/core/src/store/remote/index.ts +14 -1
- package/packages/core/src/store/remote/protocol.ts +25 -0
- package/packages/core/src/store/remote/token.ts +74 -13
- package/packages/core/src/testing/index.ts +7 -0
- package/skills/guide/SKILL.md +144 -26
- package/skills/guide/references/operator-api.md +135 -13
- package/skills/guide/references/plugin-authoring.md +10 -2
- package/skills/guide/references/remote-store-protocol.md +189 -144
- package/skills/guide/references/remote-ticket-protocol.md +111 -18
- package/skills/guide/references/setup.md +173 -16
- package/skills/guide/references/ticket-dependencies.md +1 -1
- package/skills/operate/SKILL.md +126 -36
- package/templates/hosted-service/package.json +13 -0
- package/templates/hosted-service/vercel.json +7 -0
|
@@ -9,21 +9,38 @@ and the implementation passes the shared `BuildStore` contract exported by
|
|
|
9
9
|
|
|
10
10
|
The transport, wire shapes, validation order, atomicity, lease behavior,
|
|
11
11
|
persistence requirements, and conformance bar for the protocol shipped in this
|
|
12
|
-
distribution are all defined here.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
distribution are all defined here. Protocol implementations must retain contracts for every supported client line;
|
|
13
|
+
protocol changes update this document and executable contracts together.
|
|
14
|
+
|
|
15
|
+
A service accepts stable clients from its own `major.minor` release line and
|
|
16
|
+
the two preceding minor lines in the same major. A major starts a new family:
|
|
17
|
+
X.0 accepts only X.0, X.1 accepts X.0/X.1, and X.2 onward accepts three lines.
|
|
18
|
+
Patch versions are interchangeable in both directions and must never change
|
|
19
|
+
the protocol. Upgrade the service first, then projects independently within
|
|
20
|
+
its window. Supported release lines retain their wire handlers and known
|
|
21
|
+
protocol identities; an unknown protocol is refused independently of version.
|
|
22
|
+
Malformed, missing, and prerelease identities are refused with HTTP 409.
|
|
23
|
+
A newer client is told to upgrade the service; a client below the window is
|
|
24
|
+
told to upgrade the project's Autobuild. Diagnostics name the service version
|
|
25
|
+
and supported range.
|
|
26
|
+
|
|
27
|
+
Unauthenticated `GET /health` publishes `ok`, `autobuildVersion`,
|
|
28
|
+
`protocolVersion`, and `supportedClientRange: { minVersion, maxVersionExclusive }`.
|
|
29
|
+
The range is inclusive at the minimum and exclusive at the maximum; a 0.11.x
|
|
30
|
+
service publishes `>=0.9.0 <0.12.0`. Health needs no credentials, version
|
|
31
|
+
headers, or available backend.
|
|
15
32
|
|
|
16
33
|
## 1. Transport and conventions
|
|
17
34
|
|
|
18
35
|
- The transport is JSON over HTTP or HTTPS. There is no API version prefix.
|
|
19
|
-
Point Autobuild at the server's base URL with `--store`, or
|
|
20
|
-
`
|
|
36
|
+
Point Autobuild at the server's base URL with `--store`, `AB_STORE`, or
|
|
37
|
+
`[store] url` (in that precedence order, before the local default); the shipped client appends the routes below.
|
|
21
38
|
- Every `/builds`, `/repos`, and `/sessions` request sends
|
|
22
39
|
`X-Autobuild-Version` with the exact client package version and
|
|
23
40
|
`X-Autobuild-Protocol-Version` with the remote protocol version (`3` in this
|
|
24
41
|
distribution). Before authentication,
|
|
25
|
-
body parsing, or resource lookup, the server
|
|
26
|
-
|
|
42
|
+
body parsing, or resource lookup, the server validates the release-line window and known protocol identity.
|
|
43
|
+
Unsupported or missing values receive `409 {"kind":"conflict","error":"…"}`;
|
|
27
44
|
the diagnostic names the client and server package and protocol versions.
|
|
28
45
|
- Clients send `Content-Type: application/json` for requests with bodies. Every
|
|
29
46
|
response, including errors and `null` artifact results, has
|
|
@@ -184,7 +201,7 @@ and section 7).
|
|
|
184
201
|
|
|
185
202
|
Those event catalogs evolve with Autobuild's build lifecycle rather than with
|
|
186
203
|
the storage transport. A server therefore must use or faithfully implement the
|
|
187
|
-
catalog
|
|
204
|
+
catalog behavior required by every supported client release line. The public
|
|
188
205
|
`BuildStore` type (including its typed write methods), representative valid
|
|
189
206
|
writes, and unchanged contract suite exported by `@defrex/autobuild/plugin-sdk` are the
|
|
190
207
|
package-versioned compatibility surface. Wire validation remains mandatory even
|
|
@@ -290,8 +307,8 @@ it to exist. An unknown build returns `404 not-found`.
|
|
|
290
307
|
|
|
291
308
|
| BuildStore operation | HTTP route | Request | Success |
|
|
292
309
|
|---|---|---|---|
|
|
293
|
-
| `createBuild` | `POST /builds` | `{slug: string, repo: string, repoOrigin?: string, ticket?: TicketRef, branch?: string, created?: event write}`; `slug`, `repo`, and a supplied `branch` are nonempty. `created`, when present, must be a valid `build.created` write; the server appends it as seq 1 atomically with the record (both exist or neither), and an invalid one is `422` with nothing created | `201` + `BuildRecord`;
|
|
294
|
-
| `listBuilds` | `GET /builds` | none | `200` + `BuildRecord[]
|
|
310
|
+
| `createBuild` | `POST /builds` | `{slug: string, repo: string, repoOrigin?: string, ticket?: TicketRef, branch?: string, created?: event write}`; `slug`, `repo`, and a supplied `branch` are nonempty. `created`, when present, must be a valid `build.created` write; the server appends it as seq 1 atomically with the record (both exist or neither), and an invalid one is `422` with nothing created | `201` + authoritative `BuildRecord`; project credentials receive a fresh random-suffixed slug and matching branch. Owner admin/open mode keeps exact-slug semantics with `409 conflict` for duplicates |
|
|
311
|
+
| `listBuilds` | `GET /builds` | none | `200` + `BuildRecord[]`, filtered by the server to the credential project; owner admin may list all projects. List order is unspecified |
|
|
295
312
|
| `getBuild` | `GET /builds/{slug}` | none | `200` + `BuildRecord`; absent is `404` (the shipped client maps this to `null`) |
|
|
296
313
|
| `append` | `POST /builds/{slug}/events` | event write | `201` + build event envelope |
|
|
297
314
|
| `appendIfCurrent` | `POST /builds/{slug}/events/conditional` | `{"expectedSeq": nonnegative integer, "event": event write}` | appended: `201` + build event envelope; stream advanced: `200 null` |
|
|
@@ -361,8 +378,7 @@ no protocol-version bump. The store, not the client, enforces the invariants:
|
|
|
361
378
|
A client treats `404` and `405` from either route as "no snapshot", so a client
|
|
362
379
|
newer than its store replays the log with no error, and a client older than its
|
|
363
380
|
store never calls the routes. Authentication and transport failures still
|
|
364
|
-
surface normally.
|
|
365
|
-
repository token only its own repository's, exactly as for events.
|
|
381
|
+
surface normally. Snapshot authorization follows the owning project, exactly as for events.
|
|
366
382
|
|
|
367
383
|
### The event-read bounded wait
|
|
368
384
|
|
|
@@ -399,7 +415,7 @@ artifacts, and leases. `{repo}` is one percent-encoded repository id; encode
|
|
|
399
415
|
embedded `/` characters. All resource routes authorize and require the
|
|
400
416
|
repository to exist. An unknown repository returns `404 not-found` — with one
|
|
401
417
|
carve-out: the build-digest batch read of this section is keyed to build
|
|
402
|
-
records, not the repository journal record, so it authorizes the
|
|
418
|
+
records, not the repository journal record, so it authorizes the project credential
|
|
403
419
|
but answers `200` (possibly empty) even for an unknown repository, a repository
|
|
404
420
|
without a journal record, or a repository without builds.
|
|
405
421
|
|
|
@@ -451,7 +467,7 @@ reading every build's full history per evaluation. That is a breaking wire
|
|
|
451
467
|
change, not an additive one: a server that predates it returns the old shape
|
|
452
468
|
and would fail the client's schema parse mid-request, which is why the version
|
|
453
469
|
moved — a stale peer is now rejected at the handshake (`409 conflict`) instead.
|
|
454
|
-
This is the same discipline the transport's
|
|
470
|
+
This is the same discipline the transport's supported-window rules promise:
|
|
455
471
|
wire shapes that independently implemented servers must reproduce update this
|
|
456
472
|
document and the protocol version together.
|
|
457
473
|
|
|
@@ -466,12 +482,11 @@ a ticket, keyed by `(repo, ticketId, kind, name)`. These routes live under the
|
|
|
466
482
|
repository but, unlike the journal routes, **do not require a repository
|
|
467
483
|
record**: assets can precede any journal row, so the repository-existence gate
|
|
468
484
|
does not apply. `{repo}` and `{id}` are percent-encoded path segments. Token
|
|
469
|
-
authorization
|
|
470
|
-
for its own repository, or admin); build and session tokens receive `403 auth`.
|
|
485
|
+
authorization requires a credential for that registered project, or owner admin.
|
|
471
486
|
The family is additive, so `REMOTE_STORE_PROTOCOL_VERSION` did not change.
|
|
472
487
|
|
|
473
488
|
A build also reads the assets it froze at its claim through its own build-scoped
|
|
474
|
-
routes,
|
|
489
|
+
routes, authorized against the build's actual project. The
|
|
475
490
|
server reduces the build's events to find the pinned revision, then returns that
|
|
476
491
|
exact revision, so a later replace or removal never changes the answer and a
|
|
477
492
|
finished build still answers:
|
|
@@ -785,140 +800,148 @@ These additions were purely additive in the version they landed in (protocol
|
|
|
785
800
|
conforming server implements the stream routes together with this document's
|
|
786
801
|
rules exactly as it does for the event and artifact routes.
|
|
787
802
|
|
|
788
|
-
## 7. Authentication and
|
|
803
|
+
## 7. Authentication and project authority
|
|
789
804
|
|
|
790
|
-
|
|
805
|
+
Project credentials are opaque `abp_1_<id>_<secret>` bearer values bound to one
|
|
806
|
+
registered normalized repository. The service stores only a SHA-256 digest of
|
|
807
|
+
the random secret, plus project, creator, creation time, last use, expiry and
|
|
808
|
+
revocation metadata. It checks credential status and project registration on
|
|
809
|
+
every request. Revocation takes effect on the next request without affecting
|
|
810
|
+
other credentials. Unknown, expired or revoked credentials receive `401 auth`;
|
|
811
|
+
removed projects receive `403 auth` with `project not registered`.
|
|
791
812
|
|
|
792
|
-
|
|
793
|
-
a token or event session attribution. This is intended for local development
|
|
794
|
-
and the open contract harness.
|
|
813
|
+
The deployment owner runs these commands locally with database access:
|
|
795
814
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
Authorization: Bearer <token>
|
|
815
|
+
```sh
|
|
816
|
+
ab-hosted-store credentials issue https://github.com/acme/app --user owner@example.com
|
|
817
|
+
ab-hosted-store credentials list https://github.com/acme/app
|
|
818
|
+
ab-hosted-store credentials revoke https://github.com/acme/app --id ID --user owner@example.com
|
|
801
819
|
```
|
|
802
820
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
821
|
+
Issue prints the secret once; list contains metadata only. Issue optionally
|
|
822
|
+
accepts `--ttl-seconds N` or `--expires-at ISO-8601`. For administrative/non-interactive issuance, set the returned token as
|
|
823
|
+
`AB_TOKEN`. Interactive project onboarding uses `ab connect <service-url>`;
|
|
824
|
+
it saves the credential outside the repository and commits no secret. Previous `AB_TOKEN` values and `mint operator` / `mint build` are
|
|
825
|
+
retired: the service responds that the credential format changed and points
|
|
826
|
+
to `credentials issue`. Supported old protocol shapes still work with a new
|
|
827
|
+
credential; protocol compatibility does not restore retired authentication.
|
|
810
828
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
base64url(UTF-8 JSON scope) + "." + base64url(HMAC-SHA256(secret, payloadSegment))
|
|
815
|
-
```
|
|
829
|
+
`mint admin --ttl-seconds N` creates an explicit versioned, attributed owner
|
|
830
|
+
break-glass credential. It can cross project boundaries. Keep it owner-only:
|
|
831
|
+
never give it or `AB_STORE_SECRET` to a dispatcher or build agent.
|
|
816
832
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
833
|
+
Service-owned signatures use `base64url(JSON scope) + "." +
|
|
834
|
+
base64url(HMAC-SHA256(AB_STORE_SECRET, payloadSegment))`. Signatures are checked
|
|
835
|
+
in constant time. Current strict scope shapes are:
|
|
820
836
|
|
|
821
837
|
```jsonc
|
|
822
|
-
|
|
823
|
-
{ "
|
|
824
|
-
|
|
825
|
-
// Explicit resource scope
|
|
826
|
-
{
|
|
827
|
-
"resource": { "kind": "build", "id": "build-slug" },
|
|
828
|
-
"session": "s_123",
|
|
829
|
-
"exp": 1784116800000
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
{
|
|
833
|
-
"resource": { "kind": "repo", "id": "acme/autobuild" },
|
|
834
|
-
"session": "hs_123",
|
|
835
|
-
"exp": 1784116800000
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
{
|
|
839
|
-
"resource": { "kind": "session", "id": "os_123" },
|
|
840
|
-
"session": "*",
|
|
841
|
-
"exp": 1784116800000
|
|
842
|
-
}
|
|
838
|
+
{ "version": 1, "ownerAdmin": { "user": "owner identity" }, "exp": 1800000000000 }
|
|
839
|
+
{ "version": 1, "internalOperator": { "user": "owner identity", "repo": "https://github.com/acme/app" }, "exp": 1800000000000 }
|
|
843
840
|
```
|
|
844
841
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
842
|
+
Internal operator scopes may also carry `via`, using the actor's existing
|
|
843
|
+
shape. Unknown fields are invalid. `exp` is epoch milliseconds and expiry is
|
|
844
|
+
inclusive (`exp <= now` is refused). These signatures are separate from opaque
|
|
845
|
+
project credentials and are never project onboarding credentials.
|
|
846
|
+
|
|
847
|
+
Every machine route requires Bearer authentication after version negotiation;
|
|
848
|
+
`GET /health` stays public. Explicitly unauthenticated local test servers may
|
|
849
|
+
remain open. Internal owner operator signatures cannot use raw store routes.
|
|
850
|
+
|
|
851
|
+
A project credential may read and write its repository journal, artifacts,
|
|
852
|
+
snapshots, leases, ticket assets, builds, sessions and streams. Explicit repo
|
|
853
|
+
paths and create bodies must match its project (`403 auth: wrong project`).
|
|
854
|
+
Indirect build/session/stream IDs are resolved internally and checked against
|
|
855
|
+
the resource's actual repository. Missing and foreign IDs both return the same
|
|
856
|
+
`404 not-found` error: `resource unavailable: unknown resource or wrong project`.
|
|
857
|
+
Remote clients continue mapping addressed GET 404 responses to null. Nested
|
|
858
|
+
streams must also belong to the addressed parent.
|
|
859
|
+
|
|
860
|
+
`GET /builds` is filtered by the server to the credential's project. Project
|
|
861
|
+
`POST /builds` always allocates a fresh random-suffixed slug from the requested
|
|
862
|
+
base and a matching `ab/<slug>` branch, even when the base is unused. The
|
|
863
|
+
returned BuildRecord slug and branch are authoritative for all subsequent
|
|
864
|
+
work. A foreign or missing base returns null on GET; it neither reveals
|
|
865
|
+
collisions nor blocks creation. Owner admin retains exact-slug creation and
|
|
866
|
+
all-project listing. Local BuildStore adapters retain exact-slug semantics.
|
|
867
|
+
|
|
868
|
+
Project credentials retain runner-style actor/session attribution within the
|
|
869
|
+
project; phase CLI scoped handles still constrain build and session access.
|
|
870
|
+
Authenticated raw writes cannot claim a delegated `via`. Owner-project
|
|
871
|
+
credentials also use their recorded creator for operator writes. Dispatcher
|
|
872
|
+
credentials have an immutable server-side purpose: raw store and tickets only,
|
|
873
|
+
never human operator controls. The hosted dispatcher issues one per project
|
|
874
|
+
invocation, expiring at its configured TTL, and does not revoke at tick return
|
|
875
|
+
for dispatcher plumbing. Spawned builds receive independent delegated grants. Shared content-addressed blobs
|
|
876
|
+
remain deduplicated; this is resource authorization, not blob partitioning.
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
### Delegated build and harvest credentials
|
|
880
|
+
|
|
881
|
+
`POST /credentials/delegate` uses the standard identity headers and Bearer
|
|
882
|
+
machine authentication. Its strict target is one of:
|
|
883
|
+
|
|
884
|
+
- `{kind: "build-runner", id}` for an existing build;
|
|
885
|
+
- `{kind: "build-phase", id, session}` after its `session.started` fact;
|
|
886
|
+
- `{kind: "harvest-runner", repo, id, execution, leaseHolder}` after trusted
|
|
887
|
+
preparation has atomically claimed the scan and recorded
|
|
888
|
+
`harvest.execution.authorized` for that run/execution and adopted holder;
|
|
889
|
+
- `{kind: "harvest-phase", repo, id, session}` from its runner, after the
|
|
890
|
+
matching `harvest.session.started` fact.
|
|
891
|
+
|
|
892
|
+
The response is `{token}` only. Project credentials can issue only within their
|
|
893
|
+
registered project. A runner grant may issue only an exact phase grant within
|
|
894
|
+
its own lifecycle binding; phase grants cannot delegate. Unknown targets are
|
|
895
|
+
refused. Tokens and parent secrets never become artifacts, launch envelopes or
|
|
896
|
+
execution facts. Local-store execution requires no exchange; remote execution
|
|
897
|
+
fails closed when issuance fails.
|
|
898
|
+
|
|
899
|
+
The signed strict shape is `{version: 2, delegation: {repo, kind, id, anchor,
|
|
900
|
+
session?, execution?, leaseHolder?}}`. `anchor` is the authoritative event
|
|
901
|
+
sequence; harvest grants additionally bind the authorized execution. Session
|
|
902
|
+
claims exist only on phase grants. No fixed `exp` applies to this shape:
|
|
903
|
+
signature/schema verification precedes registered-project and lifecycle checks
|
|
904
|
+
on every request, with no authorization cache. Failure to read lifecycle state
|
|
905
|
+
fails closed. Parent credential expiry does not expire an already-issued grant.
|
|
906
|
+
Project removal refuses all its grants immediately.
|
|
907
|
+
|
|
908
|
+
Build grants live until 60 seconds after durable build termination. Phase grants
|
|
909
|
+
also retire 60 seconds after their own session ends, whichever comes first.
|
|
910
|
+
Harvest grants retire 60 seconds after their run stops or their authorized
|
|
911
|
+
execution ends/releases; harvest phases additionally retire on session end.
|
|
912
|
+
The boundary is exclusive: at `end + 60_000ms` the credential is refused. This
|
|
913
|
+
window permits final transcripts, diagnostics, deposits and stream closure.
|
|
914
|
+
Pause/escalation of a build and lease expiry do not end authority. Active grants
|
|
915
|
+
survive hours-long turns, parent expiry and dispatcher/service reconstruction.
|
|
916
|
+
A durable ending never resurrects: recovered harvest work gets a new
|
|
917
|
+
`harvest.execution.authorized` identity and token.
|
|
918
|
+
|
|
919
|
+
Delegated same-project reads remain available, including journals, artifacts,
|
|
920
|
+
digests, bounded state, pinned assets and collections. Writes by a build grant
|
|
921
|
+
are confined to its build's events/deposits/artifacts, snapshots, leases and
|
|
922
|
+
streams. Phase grants also reject agent actors naming another session, while
|
|
923
|
+
preserving the CLI's trusted kernel terminals. Delegated credentials cannot
|
|
924
|
+
create builds/operator sessions, change ticket assets or repository controls,
|
|
925
|
+
or invoke operator APIs. Harvest writes require the exact run in event payloads
|
|
926
|
+
and artifact metadata; artifacts use the harvest kinds. Every artifact in an
|
|
927
|
+
atomic deposit is checked before writing. Harvest streams have an immutable
|
|
928
|
+
`harvest:<run>:<execution>:` label namespace. Addressed streams resolve their actual owner;
|
|
929
|
+
a guessed ID cannot bypass the boundary. Only the runner can heartbeat or re-acquire its exact
|
|
930
|
+
adopted repository lease holder; another current holder still fences it. Recovery acknowledgements remain dispatcher-side.
|
|
931
|
+
|
|
932
|
+
Hosted ticket access is gated by grant kind. Build-runner and build-phase grants
|
|
933
|
+
cannot call ticket routes. Harvest-phase grants may call only list-ready, get
|
|
934
|
+
and dependency-states; harvest-runner grants may additionally create proposals.
|
|
935
|
+
Other ticket mutations require project or admin authority. A denied operation
|
|
936
|
+
returns 403 naming the grant kind and operation before parsing or source construction.
|
|
937
|
+
Ticket admission and repository-keyed assets use the authenticated project;
|
|
938
|
+
delegated harvest grants retain that same project boundary.
|
|
939
|
+
|
|
940
|
+
Workspace plugin factories receive a host-only `delegateCredential` callback
|
|
941
|
+
instead of a project token; their environment excludes `AB_TOKEN`. Provisioning
|
|
942
|
+
and launch must obtain per-resource authority before running guest code and pass
|
|
943
|
+
launch tokens separately from durable execution identity. Remote providers must
|
|
944
|
+
not serialize parent credentials or fall back to forwarding them.
|
|
922
945
|
|
|
923
946
|
## 8. Atomic deposits
|
|
924
947
|
|
|
@@ -977,7 +1000,7 @@ The shipped server maps failures as follows:
|
|
|
977
1000
|
|---:|---|---|
|
|
978
1001
|
| `400` | `validation` | Invalid JSON, request body schema, malformed percent-encoding, missing/empty required `kind`, a `since`/`rev` value whose JavaScript `Number` conversion is not an integer, or a `wait` value that is not one or more ASCII digits |
|
|
979
1002
|
| `401` | `auth` | Missing bearer credentials or an invalid, malformed, badly signed, or expired token |
|
|
980
|
-
| `403` | `auth` |
|
|
1003
|
+
| `403` | `auth` | Wrong project, project not registered, or disallowed credential purpose |
|
|
981
1004
|
| `404` | `not-found` | Unknown route, unsupported method, unknown build, unknown repository, unknown session, or an unknown or foreign-scoped stream |
|
|
982
1005
|
| `409` | `conflict` | Missing/mismatched package or protocol identity, duplicate build creation, appending to a closed stream (`stream "…" is closed`), or a backing conflict reported as already existing |
|
|
983
1006
|
| `413` | `validation` | Decoded artifact or serialized stream batch exceeds the configured ceiling; a ticket-asset request or response exceeds the deployment's request ceiling; the message names the ceiling |
|
|
@@ -1103,7 +1126,8 @@ Four shipped behaviors do not add `BuildStore` routes:
|
|
|
1103
1126
|
store lifecycle; there is no close endpoint.
|
|
1104
1127
|
- `GET /health` is outside the `BuildStore` interface. It is always open and
|
|
1105
1128
|
returns exactly `200 {"ok":true,"autobuildVersion":"<package>",
|
|
1106
|
-
"protocolVersion":"3"
|
|
1129
|
+
"protocolVersion":"3", "supportedClientRange":{"minVersion":"0.9.0",
|
|
1130
|
+
"maxVersionExclusive":"0.12.0"}}`. It reports HTTP-process availability and identity,
|
|
1107
1131
|
not a deeper backing-store transaction or migration check.
|
|
1108
1132
|
|
|
1109
1133
|
There is no push/WebSocket subscription protocol, repository listing, artifact
|
|
@@ -1151,10 +1175,31 @@ Add protocol-specific tests alongside the shared contract for token encoding
|
|
|
1151
1175
|
and resource isolation, session-attributed writes, exact validation feedback,
|
|
1152
1176
|
atomic placeholder substitution, event paging, unauthenticated health, and
|
|
1153
1177
|
continuity across server restarts. The session family (section 5), the
|
|
1154
|
-
|
|
1155
|
-
(section 7) are part of the contract suite and of the protocol-specific cases. Exercise all routes through HTTP, not by
|
|
1178
|
+
project credential boundary, revocation, and delegated-write attribution are part of the contract suite and of the protocol-specific cases. Exercise all routes through HTTP, not by
|
|
1156
1179
|
calling the backing store directly.
|
|
1157
1180
|
|
|
1158
1181
|
Passing only route smoke tests is not conformance. The complete shared contract
|
|
1159
1182
|
driven through an HTTP-backed client, plus the protocol-specific cases above,
|
|
1160
1183
|
is the required compatibility test.
|
|
1184
|
+
|
|
1185
|
+
## 13. Project connection protocol
|
|
1186
|
+
|
|
1187
|
+
`ab connect <url>` uses `POST /connect/v1/requests` with the normalized origin,
|
|
1188
|
+
a random credential id and SHA-256 digest of a CLI-held secret. The response
|
|
1189
|
+
contains a ten-minute request id, separate private polling capability, short
|
|
1190
|
+
browser code, verification URL and five-second interval. `POST /connect/v1/poll`
|
|
1191
|
+
and `/cancel` require id and capability in JSON; the browser code cannot poll.
|
|
1192
|
+
Poll returns pending, approved, denied or expired plus immutable repository
|
|
1193
|
+
and credential id, never the secret. Cancel denies pending work or revokes
|
|
1194
|
+
only that request's already approved credential. Polling and authenticated
|
|
1195
|
+
browser code lookup have durable limits; 429 includes Retry-After.
|
|
1196
|
+
|
|
1197
|
+
The web-enabled origin serves `/connect` and cookie-authenticated JSON
|
|
1198
|
+
`POST /connect/v1/browser/lookup` and `/decide`. Decisions read the saved
|
|
1199
|
+
proposal, require an allowed live Better Auth session and exact Origin, and
|
|
1200
|
+
atomically register the project, install the digest and record the outcome.
|
|
1201
|
+
GET cannot approve. `GET /connect/v1/status` and `POST /connect/v1/revoke`
|
|
1202
|
+
require a project credential and operate only on its own identity; admin and
|
|
1203
|
+
internal operator credentials cannot impersonate self-service. All protocol
|
|
1204
|
+
responses are no-store/no-referrer. Connection commands are unavailable in
|
|
1205
|
+
build/harvest sessions.
|
|
@@ -2,22 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
The hosted ticket protocol exposes the complete `TicketSource` port without
|
|
4
4
|
exposing backend credentials. It shares the Autobuild and remote protocol
|
|
5
|
-
version headers with the remote BuildStore.
|
|
6
|
-
|
|
5
|
+
version headers with the remote BuildStore. The version must be in the
|
|
6
|
+
published service window and the protocol must
|
|
7
|
+
match its known release-line identity (`3` for 0.9, 0.10, and 0.11).
|
|
8
|
+
|
|
9
|
+
A service accepts stable clients from its own `major.minor` release line and
|
|
10
|
+
the two preceding minor lines in the same major. A major starts a new family:
|
|
11
|
+
X.0 accepts only X.0, X.1 accepts X.0/X.1, and X.2 onward accepts three lines.
|
|
12
|
+
Patch versions are interchangeable in both directions and must never change
|
|
13
|
+
the protocol. Upgrade the service first, then projects independently within
|
|
14
|
+
its window. Supported release lines retain their wire handlers and known
|
|
15
|
+
protocol identities; an unknown protocol is refused independently of version.
|
|
16
|
+
Malformed, missing, and prerelease identities are refused with HTTP 409.
|
|
17
|
+
A newer client is told to upgrade the service; a client below the window is
|
|
18
|
+
told to upgrade the project's Autobuild. Diagnostics name the service version
|
|
19
|
+
and supported range.
|
|
20
|
+
|
|
21
|
+
Unauthenticated `GET /health` publishes `ok`, `autobuildVersion`,
|
|
22
|
+
`protocolVersion`, and `supportedClientRange: { minVersion, maxVersionExclusive }`.
|
|
23
|
+
The range is inclusive at the minimum and exclusive at the maximum; a 0.11.x
|
|
24
|
+
service publishes `>=0.9.0 <0.12.0`. Health needs no credentials, version
|
|
25
|
+
headers, or available backend.
|
|
7
26
|
|
|
8
27
|
## Authentication and context
|
|
9
28
|
|
|
10
|
-
Every request is `POST /tickets/<operation>`, has `Content-Type:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
Every request is `POST /tickets/<operation>`, has `Content-Type: application/json`,
|
|
30
|
+
and requires `Authorization: Bearer <project credential>`. Authentication and
|
|
31
|
+
registration checks precede body parsing and source construction. Owner admin
|
|
32
|
+
is the explicit cross-project exception; internal operator signatures have no
|
|
33
|
+
ticket authority.
|
|
34
|
+
|
|
35
|
+
Project credentials are opaque `abp_1_<id>_<secret>` bearer values bound to one
|
|
36
|
+
registered normalized repository. The service stores only a SHA-256 digest of
|
|
37
|
+
the random secret, plus project, creator, creation time, last use, expiry and
|
|
38
|
+
revocation metadata. It checks credential status and project registration on
|
|
39
|
+
every request. Revocation takes effect on the next request without affecting
|
|
40
|
+
other credentials. Unknown, expired or revoked credentials receive `401 auth`;
|
|
41
|
+
removed projects receive `403 auth` with `project not registered`.
|
|
42
|
+
|
|
43
|
+
The deployment owner runs these commands locally with database access:
|
|
16
44
|
|
|
17
45
|
```sh
|
|
18
|
-
|
|
46
|
+
ab-hosted-store credentials issue https://github.com/acme/app --user owner@example.com
|
|
47
|
+
ab-hosted-store credentials list https://github.com/acme/app
|
|
48
|
+
ab-hosted-store credentials revoke https://github.com/acme/app --id ID --user owner@example.com
|
|
19
49
|
```
|
|
20
50
|
|
|
51
|
+
Issue prints the secret once; list contains metadata only. Issue optionally
|
|
52
|
+
accepts `--ttl-seconds N` or `--expires-at ISO-8601`. For administrative/non-interactive issuance, set the returned token as
|
|
53
|
+
`AB_TOKEN`. Interactive project onboarding uses `ab connect <service-url>`;
|
|
54
|
+
it saves the credential outside the repository and commits no secret. Previous `AB_TOKEN` values and `mint operator` / `mint build` are
|
|
55
|
+
retired: the service responds that the credential format changed and points
|
|
56
|
+
to `credentials issue`. Supported old protocol shapes still work with a new
|
|
57
|
+
credential; protocol compatibility does not restore retired authentication.
|
|
58
|
+
|
|
59
|
+
`mint admin --ttl-seconds N` creates an explicit versioned, attributed owner
|
|
60
|
+
break-glass credential. It can cross project boundaries. Keep it owner-only:
|
|
61
|
+
never give it or `AB_STORE_SECRET` to a dispatcher or build agent.
|
|
62
|
+
|
|
63
|
+
Hosted tickets, comments, blockers, creation-key deduplication and assets belong
|
|
64
|
+
to exactly one registered project, even when projects use the same team key.
|
|
65
|
+
Project credentials select that repository. An optional `context.repo` selector
|
|
66
|
+
must match the credential's project; mismatches are 403. Owner admin requests
|
|
67
|
+
require this selector and registration is checked before configuration is read.
|
|
68
|
+
Internal operator credentials remain forbidden on machine ticket routes.
|
|
69
|
+
|
|
21
70
|
The strict request envelope is:
|
|
22
71
|
|
|
23
72
|
```json
|
|
@@ -31,12 +80,30 @@ The strict request envelope is:
|
|
|
31
80
|
}
|
|
32
81
|
```
|
|
33
82
|
|
|
34
|
-
`teamKey`
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
83
|
+
`teamKey` remains required for older client compatibility. It and the optional
|
|
84
|
+
`claimedState` / `createState` fields are ignored as policy inputs. The service
|
|
85
|
+
loads the project's latest effective configuration from its repository journal
|
|
86
|
+
and artifact bytes for every operation. Missing or invalid configuration returns
|
|
87
|
+
an actionable 409; run the project's dispatcher to publish its configuration.
|
|
88
|
+
There is no deployment-wide lifecycle fallback.
|
|
89
|
+
|
|
90
|
+
Hosted lifecycle defaults are triage `Backlog`, claimed `Doing`, done `Done`,
|
|
91
|
+
and create/proposal equal to triage. `readyState` is required. The allowed states
|
|
92
|
+
are the distinct union of these configured roles, including custom create and
|
|
93
|
+
proposal states; roles may overlap. Historical states remain readable unchanged.
|
|
94
|
+
`tickets.doneState` controls completion and the dispatcher's merged transition.
|
|
95
|
+
|
|
96
|
+
The service never contacts Linear or relays external trackers. If the project's
|
|
97
|
+
source is Linear, file or another non-hosted source, machine HTTP, operator and
|
|
98
|
+
MCP ticket surfaces refuse with a message naming that project and source: its
|
|
99
|
+
tickets live in its own source, accessed directly by its dispatcher. This is a
|
|
100
|
+
409 conflict, not an empty queue. The dispatcher's direct adapters are unchanged.
|
|
101
|
+
|
|
102
|
+
`AB_TICKET_BACKEND` and `LINEAR_API_KEY` are removed service settings. Their
|
|
103
|
+
presence (even blank or unknown values) is ignored and emits one startup removal
|
|
104
|
+
message. `AB_TICKET_TRIAGE_STATE`, `AB_TICKET_READY_STATE`,
|
|
105
|
+
`AB_TICKET_DOING_STATE` and `AB_TICKET_DONE_STATE` are removed and ignored,
|
|
106
|
+
including malformed values. These retired settings do not prevent startup.
|
|
40
107
|
|
|
41
108
|
## Operations
|
|
42
109
|
|
|
@@ -74,6 +141,32 @@ environment values, or credentials. Client 401/403 responses become
|
|
|
74
141
|
`AuthError`; other backend messages remain actionable.
|
|
75
142
|
|
|
76
143
|
The shared `TicketSource` contract is normative for the database adapter and
|
|
77
|
-
the hosted client/server pair.
|
|
78
|
-
|
|
79
|
-
|
|
144
|
+
the hosted client/server pair. PostgreSQL migration and isolation tests prove
|
|
145
|
+
project partitions. The direct Linear client retains its separate opt-in
|
|
146
|
+
GraphQL contract; no Linear adapter ships through the hosted ticket entry point.
|
|
147
|
+
|
|
148
|
+
Every ticket returned by list, get, or create requires a boolean `resolved`,
|
|
149
|
+
with the same source-native completion semantics as `DependencyState.resolved`.
|
|
150
|
+
The client preserves it when remapping ticket references. Missing or non-boolean
|
|
151
|
+
values fail wire validation. Client and service follow the published compatibility window.
|
|
152
|
+
|
|
153
|
+
Legacy 0.9.x and 0.10.x ticket responses omit `resolved` and `blockersResolved`
|
|
154
|
+
because those clients use strict decoders without these fields. This projection
|
|
155
|
+
applies to list-ready, get, and create responses. Current clients retain the
|
|
156
|
+
current required resolution fields; domain and storage types are unchanged.
|
|
157
|
+
|
|
158
|
+
## Delegated execution credentials
|
|
159
|
+
|
|
160
|
+
Build and harvest runners exchange project authority for signed, project-bound
|
|
161
|
+
execution and phase credentials through `/credentials/delegate` (see the remote
|
|
162
|
+
store protocol). Build-runner and build-phase credentials cannot call any ticket
|
|
163
|
+
operation. Harvest-phase credentials permit only list-ready, get and
|
|
164
|
+
dependency-states. Harvest-runner credentials permit those reads plus create
|
|
165
|
+
for filing proposals. Claim, comment, transition, update, add-blocker and
|
|
166
|
+
remove-blocker require project or admin authority; their access is unchanged.
|
|
167
|
+
Denied delegated calls return 403 with an error naming the grant kind and
|
|
168
|
+
operation before body parsing or source construction. Lifecycle retirement and
|
|
169
|
+
project removal are also checked before parsing or source construction.
|
|
170
|
+
|
|
171
|
+
Ticket admission and assets use the authenticated project, including for
|
|
172
|
+
delegated harvest grants.
|