Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
888d655
feat!: stop registering; mint once, serve TLS with the workload ident…
antoinetoussaint-byte Oct 2, 2026
02ecf5f
refactor!: carry Core's capability, sign and parse none; drop the rep…
antoinetoussaint-byte Oct 2, 2026
bcaf83c
test: hold mint-once on the carrier, and report a failed attestation …
antoinetoussaint-byte Oct 2, 2026
2548c26
fix!: fail closed on every credential and identity path
antoinetoussaint-byte Oct 2, 2026
bd3abf9
fix!: hold a source's per-connection callback to the boot posture
antoinetoussaint-byte Oct 2, 2026
347b3c3
fix!: stop claiming the renderer's contract schema for a different shape
antoinetoussaint-byte Oct 2, 2026
5248f53
fix!: stop following redirects, authenticate outbound on every path, …
antoinetoussaint-byte Oct 2, 2026
16942dd
fix!: authorise callers, not just authenticate them; clear the review…
antoinetoussaint-byte Oct 3, 2026
531f565
fix: close the boundary gates' blind spot, label the guessed mint pat…
antoinetoussaint-byte Oct 3, 2026
25a037b
test: make the doubted tests fail against the old code
antoinetoussaint-byte Oct 3, 2026
3d58c97
fix: re-hold a rotated leaf to the frozen principal
antoinetoussaint-byte Oct 3, 2026
381dc38
fix!: reload outbound trust per connection, and refuse asks that stat…
antoinetoussaint-byte Oct 3, 2026
abe3d3b
fix!: validate the certificate a handshake selects, bound a busy conn…
antoinetoussaint-byte Oct 3, 2026
49baeb1
fix!: make the admission sets actually live, bound an established cal…
antoinetoussaint-byte Oct 3, 2026
4bd351e
test: make round four's checks killable by mutation
antoinetoussaint-byte Oct 3, 2026
a4f890b
test: drive the production path for the three checks a unit test coul…
antoinetoussaint-byte Oct 3, 2026
91ff109
fix!: fail closed on every route acting for a viewer, key the ceiling…
antoinetoussaint-byte Oct 3, 2026
e0f395d
test: commit the ten survivors' tests and the new round-four findings
antoinetoussaint-byte Oct 3, 2026
c3f7617
test: make the ceiling test fail for the ceiling's reason
antoinetoussaint-byte Oct 3, 2026
f511c06
fix!: ask a source for its anchor instead of fabricating a handshake,…
antoinetoussaint-byte Oct 3, 2026
4ce5044
test: cover round five's findings, and make each mutant killable
antoinetoussaint-byte Oct 3, 2026
122bfb7
test: make the pre-request watch window actually testable
antoinetoussaint-byte Oct 3, 2026
acf9c68
fix!: disable resumption, one authorization set per destination, hard…
antoinetoussaint-byte Oct 3, 2026
edfbfdb
test: kill the struck mutants, and docs for the round-five changes
antoinetoussaint-byte Oct 3, 2026
caaf98e
test: probe the seam gate's indirection directly
antoinetoussaint-byte Oct 3, 2026
1289fbf
test: close the three gaps the ledger found, two of them 'passes via …
antoinetoussaint-byte Oct 3, 2026
b0d7344
fix!: stop minting per viewer request, record a refusal wherever it l…
antoinetoussaint-byte Oct 3, 2026
5a186f1
docs: the round-six behaviour changes
antoinetoussaint-byte Oct 3, 2026
e4fef36
test: close the four gaps in the round-six ledger
antoinetoussaint-byte Oct 3, 2026
ba2a255
docs: apply Opus 2.8, which I reported as done and had not applied
antoinetoussaint-byte Oct 3, 2026
1162a48
fix!: one credential controller, admission before a source's verifier…
antoinetoussaint-byte Oct 3, 2026
3f6afcd
test: one per round-seven finding, and two that were wrong first
antoinetoussaint-byte Oct 3, 2026
5d00510
test: close the two real gaps the ledger found, and say what the othe…
antoinetoussaint-byte Oct 3, 2026
960b21d
chore(scout): close the boot harness's port race by handing over the …
antoinetoussaint-byte Oct 3, 2026
1bef1e8
chore(scout): clean the repository's lint and vet, and fix a second t…
antoinetoussaint-byte Oct 3, 2026
e118f60
fix!: a source never holds the certificates this listener authenticat…
antoinetoussaint-byte Oct 4, 2026
a2993bf
fix!: hold an established connection to the identity it was admitted as
antoinetoussaint-byte Oct 4, 2026
257543a
test: the authority recheck after the ask had nothing holding it
antoinetoussaint-byte Oct 4, 2026
93878d8
test: the recheck before the ask had no property of its own under test
antoinetoussaint-byte Oct 4, 2026
194599d
Merge main: integrate ServeMCP with the cutover, which refuses it twice
antoinetoussaint-byte Oct 4, 2026
ff63b0f
Merge main again: #49 makes the MCP issuer declared configuration
antoinetoussaint-byte Oct 4, 2026
129ec01
fix!: an MCP tool is held to this execution's credential, and the MCP…
antoinetoussaint-byte Oct 4, 2026
0d24c45
fix!: an MCP request is refused at the HTTP boundary, and a connectio…
antoinetoussaint-byte Oct 4, 2026
f680894
fix!: the runtime's own error is sanitized before a tool ever holds it
antoinetoussaint-byte Oct 4, 2026
19bc1bb
test: the method-layer MCP gate had nothing holding it once the HTTP …
antoinetoussaint-byte Oct 4, 2026
1e9472e
fix!: the MCP allowlist names exact methods, the installation comes f…
antoinetoussaint-byte Oct 4, 2026
d9b82ef
fix!: re-apply the MCP allowlist and the JSON-RPC code, which 1e9472e…
antoinetoussaint-byte Oct 4, 2026
6edaf6f
chore!: pin core v0.9.0 and sdk-go v0.3.0, which is an API migration …
antoinetoussaint-byte Oct 4, 2026
ff764c4
test: the plaintext-mint boot refusal was satisfied by the SDK's own
antoinetoussaint-byte Oct 4, 2026
b4e90d6
test: the JSON-RPC code on the MCP refusal had nothing asserting it
antoinetoussaint-byte Oct 4, 2026
4408997
fix!: a workload credential sealing no execution is refused everywhere
antoinetoussaint-byte Oct 4, 2026
9cc7e01
fix: the mint is dialled under the anchor as it is now, not as it was…
antoinetoussaint-byte Oct 4, 2026
9c7994a
fix: sanitize the mint error ForModule's exported sibling relayed, an…
antoinetoussaint-byte Oct 4, 2026
355c787
fix!: hold the credential to the EXECUTION it was first sealed to, an…
antoinetoussaint-byte Oct 4, 2026
041cd3c
fix: redactedURL printed an opaque URL verbatim, and a comment still …
antoinetoussaint-byte Oct 4, 2026
8934131
fix!: a tool result never carries the runtime's internals, and the bo…
antoinetoussaint-byte Oct 4, 2026
632c342
docs: state the mint hop's residual posture exactly, and draft the sd…
antoinetoussaint-byte Oct 4, 2026
7da75f6
docs: AGENTS.md carries the mint hop's residual, which two earlier ed…
antoinetoussaint-byte Oct 4, 2026
26cd53e
fix!: revert two mutants my own commits captured, and say how they go…
antoinetoussaint-byte Oct 4, 2026
2a023d3
docs: the README promised a sentinel this PR's own re-pin deleted
antoinetoussaint-byte Oct 4, 2026
db61fff
fix!: close the five gate shapes and the capability decode, and make …
antoinetoussaint-byte Oct 4, 2026
5caef32
fix!: three tests claimed coverage they did not have, and the harness…
antoinetoussaint-byte Oct 4, 2026
b0e5635
fix: the renderedType wrapper went unused when its callers moved to r…
antoinetoussaint-byte Oct 4, 2026
e2bb369
fix!: the mint hop is held to its peer again, through sdk-go#51 rathe…
antoinetoussaint-byte Oct 5, 2026
bd582a7
fix!: round thirteen's majors 2-5 — the gate resolves local names, an…
antoinetoussaint-byte Oct 5, 2026
9b0cedc
test: the mint's anchor is passed as a reader, which no test could te…
antoinetoussaint-byte Oct 5, 2026
5d4777c
fix: the renewal test's margin depended on the boot being fast, and C…
antoinetoussaint-byte Oct 5, 2026
00419e5
fix!: the mint presented a rotated identity unchecked, and a checked …
antoinetoussaint-byte Oct 5, 2026
d04fdb3
fix!: the decode gate reads destinations, the handler gate reads fiel…
antoinetoussaint-byte Oct 5, 2026
de99267
fix!: freeze the declared ceiling, follow a decode through an assignm…
antoinetoussaint-byte Oct 5, 2026
900fdfd
fix: four tests round fifteen doubted, and the one I could not isolat…
antoinetoussaint-byte Oct 5, 2026
2b4cf48
fix!: the gate's whole-capability probes named a type the SDK does no…
antoinetoussaint-byte Oct 5, 2026
87b3bd6
test: the F1 probes never landed, so the gate's new rules had no comm…
antoinetoussaint-byte Oct 5, 2026
c26355b
fix!: the boundary gates are built on go/types, so there is no list l…
antoinetoussaint-byte Oct 5, 2026
fba16fa
test: one assertion per capability shape, and the embedded shape a mu…
antoinetoussaint-byte Oct 5, 2026
59a245f
fix!: round sixteen's four findings, at their causes
antoinetoussaint-byte Oct 5, 2026
69d390d
test: an ordinary conflict must retire nothing, which nothing asserted
antoinetoussaint-byte Oct 5, 2026
0db1091
test: the one decode shape only the name rule catches, so both rules …
antoinetoussaint-byte Oct 5, 2026
5a5b458
fix!: the gates follow values and instantiations, and a 409 is no lon…
antoinetoussaint-byte Oct 5, 2026
6394cf9
test: the gate controls discriminate, and the dead supersede primitiv…
antoinetoussaint-byte Oct 5, 2026
2690c28
docs: the README described the eviction that was deleted
antoinetoussaint-byte Oct 5, 2026
f439a30
fix!: one traversal, a fixed point over resolved types, and the MCP s…
antoinetoussaint-byte Oct 5, 2026
c9abd09
test: the walk subsumes the explicit dereference, so the redundant st…
antoinetoussaint-byte Oct 5, 2026
3783c03
fix: the opaque-parameter refusal printed its own arguments in the wr…
antoinetoussaint-byte Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
601 changes: 530 additions & 71 deletions AGENTS.md

Large diffs are not rendered by default.

1,110 changes: 863 additions & 247 deletions README.md

Large diffs are not rendered by default.

1,709 changes: 1,709 additions & 0 deletions boot_test.go

Large diffs are not rendered by default.

297 changes: 297 additions & 0 deletions conformance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
package solution

import (
"context"
"errors"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"

corework "github.com/codefly-dev/core/workcontext"
"github.com/codefly-dev/sdk-go/workcontext"
)

// Core's published conformance fixtures, driven through this package's
// *carriage* boundary.
//
// The earlier claim was that there is no conformance kit for this package to
// run, on the grounds that the kit is for verifiers and this runtime verifies
// nothing. That was half right and it was the convenient half. Minting with
// Core's fixture identities — which is what the rest of the suite does — shows
// successful carriage and nothing else: it never presents the kit's negative
// fixtures, so the refusals this package *is* responsible for were never
// exercised against the bytes Core publishes for exactly that purpose.
//
// What this runtime is responsible for is the boundary it owns: a capability it
// is handed gets carried, and one it cannot carry is refused before a call is
// made on it. So each fixture is offered to the carrier, and the assertion is
// the division of labour:
//
// - a capability with no readable seal, and a token in another encoding, are
// refused HERE, with Core's own sentinels. Those are properties of the
// bytes in hand, and the moment to find them out is before a request is
// sent rather than at the far end, where the refusal would name an
// installation mismatch for a capability that never named an installation;
// - a tampered payload, an unknown key, a wrong audience and a sealed value
// the issuer has moved past are NOT refused here, and must not be. They
// are judgements against live state and a signature, which only a verifier
// holding the issuer's four sources can make. A carrier that refused them
// would be claiming a strength it does not have — the silent-downgrade
// failure this whole single-implementation effort exists to end.
//
// Running the kit this way is what makes that division checkable rather than
// asserted in a comment: if Core adds a fixture whose refusal belongs to the
// carrier, this test starts failing and the carrier has to grow.
func TestCoreConformanceFixturesThroughTheCarrier(t *testing.T) {
fixtures, err := corework.Fixtures(time.Now())
if err != nil {
t.Fatalf("core conformance fixtures: %v", err)
}
if len(fixtures) < 39 {
t.Fatalf("core published %d fixtures; the kit is 39 at core v0.9.0, so this test is reading a trimmed set", len(fixtures))
}

var carriedCount, refusedCount int
for _, fixture := range fixtures {
t.Run(fixture.Name, func(t *testing.T) {
// Through *this package's* boundary, not the SDK's.
//
// This used to call workcontext.Attach directly, which a second
// reviewer rightly said is not "through its carriage boundary": it
// exercised sdk-go and touched no line of this runtime, so the
// division of labour it asserts could have been true of the SDK
// while this package did something else entirely. What carries a
// capability here is bearerTransport, on a gateway derived for a
// module, and that is what is driven now — the capability is put in
// the cache the way a mint would, and a real request is made.
request, err := carryThroughThisRuntime(t, fixture.Token)

side, classified := boundary[fixture.Name]
if !classified {
t.Fatalf("core publishes a fixture this test does not classify (%s: %s). Decide which side of the carriage boundary its refusal belongs to and say so in `boundary`: a fixture nobody classified used to land in a bucket that accepted either answer, which is eight of twenty-one fixtures asserting nothing.",
fixture.Name, fixture.Reason)
}
switch side {
case carried:
if err != nil {
t.Fatalf("the carrier refused %s (%s) with %v: %s", fixture.Name, fixture.Reason, err, side.why())
}
if got := request.Header.Get(workcontext.HeaderName); got != fixture.Token {
t.Error("the capability did not reach the request header")
}
if request.Header.Get(workcontext.InstallationIDHeaderName) == "" {
t.Error("the installation the capability is sealed to did not travel beside it")
}
carriedCount++

case refusedHere:
if err == nil {
t.Fatalf("the carrier accepted %s (%s): %s", fixture.Name, fixture.Reason, side.why())
}
// With one of Core's sentinels, never an error of this
// package's own: the classification is Core's, and a refusal
// this runtime invented a taxonomy for is the beginning of the
// second implementation this module is gated against.
//
// ErrUnsealed was in this list and core v0.9.0 deleted it, for
// a reason worth repeating: protovalidate refuses a capability
// with no seal before anything in core's own reader is
// reached, so no branch could produce that sentinel — and "a
// sentinel no branch can produce is worse than no sentinel,
// because a consumer writes a handler for it and the handler
// never runs". An unsealed capability arrives as ErrInvalid.
if !errors.Is(err, corework.ErrInvalid) && !errors.Is(err, corework.ErrNotACoreToken) {
t.Errorf("the carrier refused %s with %v, which is none of Core's sentinels for a capability it cannot read", fixture.Name, err)
}
refusedCount++
}
})
}

if carriedCount == 0 || refusedCount == 0 {
t.Errorf("the kit exercised carried=%d refused-here=%d: both have to be non-zero or this test is checking one side of the boundary",
carriedCount, refusedCount)
}
if carriedCount+refusedCount != len(fixtures) {
t.Errorf("classified %d of %d fixtures", carriedCount+refusedCount, len(fixtures))
}
}

// carriage is which side of this runtime's boundary a fixture's refusal belongs
// to.
type carriage int

const (
// carried: the capability goes on the request. Either it is sound, or
// judging it needs something this runtime does not hold.
carried carriage = iota
// refusedHere: the capability never goes on a request, because what is
// wrong with it is a property of the bytes in hand.
refusedHere
)

func (c carriage) why() string {
if c == carried {
return "that judgement needs the issuer's live revision, replay, grant and seal sources, which this runtime does not hold — and a carrier that refused it would be claiming a strength it does not have, which is the silent downgrade this single-implementation effort exists to end"
}
return "this is a property of the bytes in hand, so the moment to find it out is before a request is sent: refused at the far end it would name an installation mismatch for a capability that never named an installation"
}

// boundary classifies every fixture Core publishes, by name, from Core's own
// stated reason for each.
//
// It is a table and not a rule because the previous version was a rule with a
// default branch, and the default accepted *either* answer for the eight
// fixtures Core refuses as ErrInvalid — a bucket spanning "not two base64
// segments", which this runtime must catch, and "one byte of the payload
// changed", which only a signature can catch. Eight of twenty-one fixtures
// therefore asserted nothing, and a reviewer showed the committed test passing
// with the runtime's carriage validation removed.
//
// The division itself is unchanged and is Core's: what is visible in the bytes
// is refused here, and what needs the issuer's four sources is carried.
var boundary = map[string]carriage{
// Sound capabilities. They travel.
"session": carried,
"operation": carried,
"delegated": carried,
"delegated-operation": carried,
"grant": carried,

// Not the token shape at all: no separator, nothing after it, a payload
// that is not base64, or nothing. Visible without a key, so this runtime
// refuses them rather than sending a request that cannot succeed.
"empty-token": refusedHere,
"no-separator": refusedHere,
"separator-only": refusedHere,
"payload-not-b64": refusedHere,

// No readable seal. The carrier has to put the installation beside the
// capability, and a capability that names none cannot be carried — Core
// calls the one with a partial seal invalid and this runtime calls it
// unsealed, which is the same refusal reached one field earlier.
"missing-seal": refusedHere,
"seal-without-installation": refusedHere,

// A genuinely signed token in another encoding. Refused as a foreign format
// before any signature is considered, which is the diagnosis Core's own
// fixture exists to protect.
"foreign-encoding": refusedHere,

// Signature judgements. A tampered payload and a key the verifier does not
// hold are indistinguishable from a sound capability without the issuer's
// keys, and an audience is a claim inside the payload — this runtime holds
// none of what it would take to say so, and the far end says so for a
// living.
"tampered-payload": carried,
"unknown-key": carried,
"another-audience": carried,

// Live state moved under a sound capability: an installation revision, a
// build incarnation, a principal epoch, a binding revision. Judging these
// is exactly what a carrier cannot do.
"stale-installation-revision": carried,
"future-installation-revision": carried,
"unknown-installation": carried,
"stale-build-incarnation": carried,
"stale-principal-epoch": carried,
"wrong-binding-revision": carried,
// --- core v0.9.0 added eighteen fixtures. Each is classified by the one
// question this boundary turns on: is the defect visible in the BYTES IN
// HAND, or is it a judgement against the issuer's live state and keys?

// Schema violations, refused by protovalidate inside core's own decode —
// which this runtime's carrier reaches, because reading the sealed
// installation decodes the claims. A counter at zero names nothing and
// would compare equal to a source holding nothing; half a binding and half
// an execution are not one; an unknown field nested in the seal lets data
// ride inside a signed credential nothing here reads. All structural, all
// decidable without a key.
"zero-principal-epoch": refusedHere,
"zero-installation-revision": refusedHere,
"zero-build-incarnation": refusedHere,
"seal-half-execution": refusedHere,
"partial-operation-binding": refusedHere,
"actor-without-epoch": refusedHere,
"unknown-field": refusedHere,

// Judgements this runtime cannot make. The issuer's identity is a claim
// and the key is not the trust decision; a validity window needs a clock
// this carrier does not apply; and every remaining one compares a sealed
// value against live state only the issuer holds — an approved build, a
// principal's epoch, an authorization revision, a binding's revision,
// incarnation, grantee or installation. A carrier that refused any of
// these would be claiming a strength it does not have.
"another-issuer": carried,
"expired": carried,
"not-yet-valid": carried,
"execution-missing": carried,
"unapproved-build": carried,
"stale-actor-epoch": carried,
"superseded-authorization-revision": carried,
"revoked-operation-binding": carried,
"wrong-binding-incarnation": carried,
"binding-of-another-principal": carried,
"binding-in-another-installation": carried,
}

// carryThroughThisRuntime drives one capability through this package's carriage
// path and returns the request that reached the far end, or the refusal that
// stopped it before anything was sent.
//
// The path is the real one: a Gateway derived by ForModule holds a delegation,
// bearerTransport resolves the capability per round trip and attaches it, and
// the request only leaves if that succeeds. A capability this runtime cannot
// carry therefore has to fail here, before a module is called — which is the
// property the kit is being used to check, and the one that cannot be observed
// by calling the SDK directly.
func carryThroughThisRuntime(t *testing.T, token string) (*http.Request, error) {
t.Helper()
var reached *http.Request
far := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reached = r.Clone(context.Background())
writeJSON(w, http.StatusOK, map[string]string{"ok": "yes"})
}))
defer far.Close()

gateway := newGateway(far.URL, "Bearer viewer", "org-1", "session-1")
// The capability as a mint would have left it in the cache, so the
// transport resolves this exact token rather than minting another.
const key = "conformance"
gateway.delegation = &delegation{key: key}
if _, err := gateway.contexts.resolve(context.Background(), key,
func(context.Context) (string, time.Time, error) {
return token, time.Now().Add(time.Hour), nil
}); err != nil {
// The cache refused to hold it at all, which is this runtime refusing
// to carry it — the same answer as a refusal at attach time.
return nil, err
}

request, err := http.NewRequest(http.MethodPost, far.URL+"/v1/things/search", nil)
if err != nil {
t.Fatal(err)
}
resp, err := gateway.HTTPClient().Do(request)
if err != nil {
return nil, unwrapURLError(err)
}
_ = resp.Body.Close()
if reached == nil {
t.Fatal("the far end was not reached and nothing refused the call")
}
return reached, nil
}

// unwrapURLError returns what a transport refusal actually was: http.Client
// wraps a RoundTrip error in *url.Error, which errors.Is sees through but a
// reader of the message does not.
func unwrapURLError(err error) error {
var urlErr *url.Error
if errors.As(err, &urlErr) {
return urlErr.Err
}
return err
}
Loading
Loading