carlos / platform Public

Validate private maintenance launch contexts and checkpoint bindings

Paul Campbell pushed by paul@keymail.dev 387908f1afb868ac3466a0a7940d7caa34e24303 parent 006e782
6 files changed, +487 −0
  • implementation/maintenance-worker-progress.json +16 −0
  • internal/maintenance/launch.go +218 −0
  • internal/maintenance/launch_test.go +175 −0
  • internal/maintenance/launch_utf8.go +48 −0
  • internal/maintenance/testdata/launch-v2-binding.json +1 −0
  • internal/maintenance/testdata/launch-v2.json +29 −0
diff --git a/implementation/maintenance-worker-progress.json b/implementation/maintenance-worker-progress.json
new file mode 100644
index 000000000..d4dc9767c
--- /dev/null
+++ b/implementation/maintenance-worker-progress.json
@@ -0,0 +1,16 @@
+{
+ "scope": "Complete supported maintenance dispatch needed by Oficina provisioning; no live invocation until all approved launch gates are implemented and reviewed",
+ "base": "006e7825",
+ "current_slice": "Strict typed private launch-context codec for approved mutable v1 and checkpoint-checker v2",
+ "authority": "Decoded context is metadata only. It cannot construct a retained lease, executor, lifecycle claim, approval or trusted invocation.",
+ "remaining": [
+ "Verified pinned-artifact launch and cancellable bounded pipe supervisor using strict cgroup handles",
+ "Retained worker lease/executor and closed-checkpoint preparation proof",
+ "Home startup adapter binding signed input, image, descriptors and opened database",
+ "Owner-reviewed prepare/approval/status/result/retry/resume CLI and console workflow",
+ "Private encrypted request/result publication with authenticated worker provenance",
+ "Integrated native tests and Fable review before worker mounting",
+ "Preview owner-approved provisioning, restore drill, and public gateway cutover"
+ ],
+ "verification": ["Required closed fields and exact numeric ranges", "Canonical timestamps, identifiers and binding digests", "UTF-8 and surrogate validity", "8KiB hard limit and overflow detection", "Nested v2 checkpoint evidence bindings", "No command mounting or execution authority from decoded bytes"]
+}
diff --git a/internal/maintenance/launch.go b/internal/maintenance/launch.go
new file mode 100644
index 000000000..a0cde583c
--- /dev/null
+++ b/internal/maintenance/launch.go
@@ -0,0 +1,218 @@
+package maintenance
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "io"
+ "strings"
+ "time"
+ "unicode/utf8"
+
+ "github.com/carlosframework/platform/internal/controlname"
+ "github.com/carlosframework/platform/internal/maintenanceguard"
+)
+
+const MaxLaunchBytes = 8192
+
+var ErrLaunch = errors.New("maintenance: invalid private launch context")
+
+// LaunchContext is private transport metadata, never proof of approval,
+// exclusion, lease ownership or a trusted worker invocation.
+type LaunchContext struct {
+ Version int `json:"version"`
+ Account string `json:"account"`
+ App string `json:"app"`
+ Host string `json:"host"`
+ RequestID string `json:"request_id"`
+ AttemptID string `json:"attempt_id"`
+ RequestDigest string `json:"request_digest"`
+ RecordIdentity string `json:"record_identity"`
+ ArtifactDigest string `json:"artifact_digest"`
+ EnvironmentEnvelopeDigest string `json:"environment_envelope_digest"`
+ GuardGeneration int64 `json:"guard_generation"`
+ InputSHA256 string `json:"input_sha256"`
+ DatabasePath string `json:"database_path"`
+ DatabaseDevice uint64 `json:"database_device"`
+ DatabaseInode uint64 `json:"database_inode"`
+ ExecutionDeadline string `json:"execution_deadline"`
+ ExecutionKind string `json:"execution_kind,omitempty"`
+ OperationID string `json:"operation_id,omitempty"`
+ ExpectedStateSHA256 string `json:"expected_state_sha256,omitempty"`
+ ClosedCheckpoint *ClosedCheckpoint `json:"closed_checkpoint,omitempty"`
+}
+
+type ClosedCheckpoint struct {
+ Version int `json:"version"`
+ PreparationID string `json:"preparation_id"`
+ CompletedAt string `json:"completed_at"`
+ MainLength int64 `json:"main_length"`
+ MainSHA256 string `json:"main_sha256"`
+ EvidenceSHA256 string `json:"evidence_sha256"`
+}
+
+// ReadLaunch requires EOF and consumes at most the bound plus one byte.
+// The worker/adapter must separately bound a reader that can block.
+func ReadLaunch(r io.Reader) (LaunchContext, error) {
+ if r == nil {
+ return LaunchContext{}, ErrLaunch
+ }
+ raw, err := io.ReadAll(io.LimitReader(r, MaxLaunchBytes+1))
+ if err != nil {
+ return LaunchContext{}, ErrLaunch
+ }
+ return DecodeLaunch(raw)
+}
+
+func DecodeLaunch(raw []byte) (LaunchContext, error) {
+ fail := func() (LaunchContext, error) { return LaunchContext{}, ErrLaunch }
+ if len(raw) == 0 || len(raw) > MaxLaunchBytes || !utf8.Valid(raw) || !validSurrogates(raw) {
+ return fail()
+ }
+ fields, err := launchObject(raw)
+ if err != nil {
+ return fail()
+ }
+ var version int
+ if json.Unmarshal(fields["version"], &version) != nil {
+ return fail()
+ }
+ names := strings.Fields("version account app host request_id attempt_id request_digest record_identity artifact_digest environment_envelope_digest guard_generation input_sha256 database_path database_device database_inode execution_deadline")
+ switch version {
+ case 1:
+ case 2:
+ names = append(names, strings.Fields("execution_kind operation_id expected_state_sha256 closed_checkpoint")...)
+ cp, err := launchObject(fields["closed_checkpoint"])
+ if err != nil || !exactLaunchFields(cp, strings.Fields("version preparation_id completed_at main_length main_sha256 evidence_sha256")) {
+ return fail()
+ }
+ default:
+ return fail()
+ }
+ if !exactLaunchFields(fields, names) {
+ return fail()
+ }
+ var c LaunchContext
+ if json.Unmarshal(raw, &c) != nil || !c.valid() {
+ return fail()
+ }
+ return c, nil
+}
+
+func EncodeLaunch(c LaunchContext) ([]byte, error) {
+ if !c.valid() {
+ return nil, ErrLaunch
+ }
+ raw, err := json.Marshal(c)
+ if err != nil || len(raw) > MaxLaunchBytes {
+ return nil, ErrLaunch
+ }
+ return raw, nil
+}
+
+func launchObject(raw []byte) (map[string]json.RawMessage, error) {
+ dec := json.NewDecoder(bytes.NewReader(raw))
+ tok, err := dec.Token()
+ if err != nil || tok != json.Delim('{') {
+ return nil, ErrLaunch
+ }
+ out := map[string]json.RawMessage{}
+ for dec.More() {
+ tok, err = dec.Token()
+ if err != nil {
+ return nil, ErrLaunch
+ }
+ key, ok := tok.(string)
+ if !ok {
+ return nil, ErrLaunch
+ }
+ if _, exists := out[key]; exists {
+ return nil, ErrLaunch
+ }
+ var value json.RawMessage
+ if dec.Decode(&value) != nil || bytes.Equal(bytes.TrimSpace(value), []byte("null")) {
+ return nil, ErrLaunch
+ }
+ out[key] = value
+ }
+ tok, err = dec.Token()
+ if err != nil || tok != json.Delim('}') {
+ return nil, ErrLaunch
+ }
+ if _, err = dec.Token(); err != io.EOF {
+ return nil, ErrLaunch
+ }
+ return out, nil
+}
+func exactLaunchFields(fields map[string]json.RawMessage, names []string) bool {
+ if len(fields) != len(names) {
+ return false
+ }
+ for _, name := range names {
+ if _, ok := fields[name]; !ok {
+ return false
+ }
+ }
+ return true
+}
+func canonicalStamp(s string) (time.Time, bool) {
+ t, err := time.Parse(time.RFC3339Nano, s)
+ return t, err == nil && len(s) <= 30 && t.UTC().Format(time.RFC3339Nano) == s
+}
+func (c LaunchContext) valid() bool {
+ if !controlname.ValidAccount(c.Account) || !controlname.ValidApp(c.App) || !controlname.ValidHostShape(c.Host) {
+ return false
+ }
+ for _, id := range []string{c.RequestID, c.AttemptID} {
+ if !maintenanceguard.ValidID(id) {
+ return false
+ }
+ }
+ for _, digest := range []string{c.RequestDigest, c.RecordIdentity, c.ArtifactDigest, c.EnvironmentEnvelopeDigest, c.InputSHA256} {
+ if !maintenanceguard.ValidDigest(digest) {
+ return false
+ }
+ }
+ if c.GuardGeneration <= 0 || c.DatabaseDevice == 0 || c.DatabaseInode == 0 {
+ return false
+ }
+ if len(c.DatabasePath) == 0 || len(c.DatabasePath) > 1024 || c.DatabasePath[0] != '/' || strings.ContainsRune(c.DatabasePath, 0) || !utf8.ValidString(c.DatabasePath) {
+ return false
+ }
+ deadline, ok := canonicalStamp(c.ExecutionDeadline)
+ if !ok {
+ return false
+ }
+ switch c.Version {
+ case 1:
+ return c.ExecutionKind == "" && c.OperationID == "" && c.ExpectedStateSHA256 == "" && c.ClosedCheckpoint == nil
+ case 2:
+ if c.ExecutionKind != "compatibility-check" || !maintenanceguard.ValidID(c.OperationID) || !maintenanceguard.ValidDigest(c.ExpectedStateSHA256) || c.ClosedCheckpoint == nil {
+ return false
+ }
+ p := c.ClosedCheckpoint
+ completed, ok := canonicalStamp(p.CompletedAt)
+ if !ok || !completed.Before(deadline) || p.Version != 1 || !maintenanceguard.ValidID(p.PreparationID) || p.MainLength <= 0 || p.MainLength > 268435456 || !maintenanceguard.ValidDigest(p.MainSHA256) || !maintenanceguard.ValidDigest(p.EvidenceSHA256) {
+ return false
+ }
+ return p.EvidenceSHA256 == c.checkpointDigest()
+ }
+ return false
+}
+
+// CheckpointDigest correlates private bindings; it does not certify that a
+// checkpoint, writer exclusion or read-only namespace actually exists.
+func (c LaunchContext) CheckpointDigest() (string, error) {
+ if c.Version != 2 || c.ClosedCheckpoint == nil {
+ return "", ErrLaunch
+ }
+ return c.checkpointDigest(), nil
+}
+func (c LaunchContext) checkpointDigest() string {
+ p := c.ClosedCheckpoint
+ raw, _ := json.Marshal([]any{"carlos/closed-checkpoint/v1", c.Account, c.App, c.Host, c.OperationID, c.RequestID, c.AttemptID, c.RequestDigest, c.RecordIdentity, c.ArtifactDigest, c.EnvironmentEnvelopeDigest, c.GuardGeneration, c.InputSHA256, c.ExpectedStateSHA256, c.DatabasePath, c.DatabaseDevice, c.DatabaseInode, c.ExecutionDeadline, p.PreparationID, p.CompletedAt, p.MainLength, p.MainSHA256})
+ sum := sha256.Sum256(raw)
+ return hex.EncodeToString(sum[:])
+}
diff --git a/internal/maintenance/launch_test.go b/internal/maintenance/launch_test.go
new file mode 100644
index 000000000..55f156c3e
--- /dev/null
+++ b/internal/maintenance/launch_test.go
@@ -0,0 +1,175 @@
+package maintenance
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "os"
+ "strings"
+ "testing"
+)
+
+func launchFixture(t *testing.T) ([]byte, LaunchContext) {
+ t.Helper()
+ raw, err := os.ReadFile("testdata/launch-v2.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ c, err := DecodeLaunch(raw)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return raw, c
+}
+func TestLaunchExactIntegersAndIndependentCheckpointVector(t *testing.T) {
+ _, c := launchFixture(t)
+ if c.DatabaseDevice != ^uint64(0) || c.DatabaseInode != 9007199254740993 || c.GuardGeneration != 9223372036854775807 {
+ t.Fatal("integer precision lost")
+ }
+ raw, err := EncodeLaunch(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ again, err := ReadLaunch(bytes.NewReader(raw))
+ if err != nil || again.DatabaseDevice != c.DatabaseDevice || again.DatabaseInode != c.DatabaseInode {
+ t.Fatal("round trip lost identity", err)
+ }
+ binding, err := os.ReadFile("testdata/launch-v2-binding.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Contains(binding, []byte(`18446744073709551615`)) || !bytes.Contains(binding, []byte(`\u003chome\u003e\u0026`)) {
+ t.Fatal("independent vector lacks precision/escaping cases")
+ }
+ c.Version = 1
+ c.ExecutionKind = ""
+ c.OperationID = ""
+ c.ExpectedStateSHA256 = ""
+ c.ClosedCheckpoint = nil
+ raw, err = EncodeLaunch(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := DecodeLaunch(raw); err != nil {
+ t.Fatal(err)
+ }
+ c.ExecutionKind = "compatibility-check"
+ if _, err := EncodeLaunch(c); err == nil {
+ t.Fatal("v1 accepted checker fields")
+ }
+}
+func TestLaunchRefusesEachMissingOrAmbiguousField(t *testing.T) {
+ raw, _ := launchFixture(t)
+ var fields map[string]json.RawMessage
+ if err := json.Unmarshal(raw, &fields); err != nil {
+ t.Fatal(err)
+ }
+ for key := range fields {
+ t.Run(key, func(t *testing.T) {
+ var f map[string]json.RawMessage
+ json.Unmarshal(raw, &f)
+ delete(f, key)
+ bad, _ := json.Marshal(f)
+ if _, err := DecodeLaunch(bad); err == nil {
+ t.Fatal("missing field accepted")
+ }
+ f[strings.ToUpper(key)] = fields[key]
+ bad, _ = json.Marshal(f)
+ if _, err := DecodeLaunch(bad); err == nil {
+ t.Fatal("case alias accepted")
+ }
+ f[key] = json.RawMessage("null")
+ delete(f, strings.ToUpper(key))
+ bad, _ = json.Marshal(f)
+ if _, err := DecodeLaunch(bad); err == nil {
+ t.Fatal("null accepted")
+ }
+ })
+ }
+ for _, bad := range [][]byte{append(append([]byte{}, raw...), []byte(` {}`)...), append([]byte(`{"version":2,`), bytes.TrimSpace(raw)[1:]...), bytes.Replace(raw, []byte(`"database_inode": 9007199254740993`), []byte(`"database_inode": 18446744073709551616`), 1), bytes.Replace(raw, []byte(`"guard_generation": 9223372036854775807`), []byte(`"guard_generation": 1e3`), 1), bytes.Replace(raw, []byte(`"database_inode": 9007199254740993`), []byte(`"database_inode": 1.0`), 1), bytes.Replace(raw, []byte(`"version": 2`), []byte(`"version": 2.0`), 1), bytes.Replace(raw, []byte(`"main_length": 268435456`), []byte(`"main_length": 268435457`), 1)} {
+ if _, err := DecodeLaunch(bad); err == nil {
+ t.Fatalf("malformed context accepted: %s", bad)
+ }
+ }
+}
+func TestLaunchCheckpointCannotBeTransplanted(t *testing.T) {
+ _, c := launchFixture(t)
+ raw, _ := EncodeLaunch(c)
+ var fields map[string]json.RawMessage
+ json.Unmarshal(raw, &fields)
+ for key, value := range fields {
+ if key == "closed_checkpoint" || key == "version" || key == "execution_kind" {
+ continue
+ }
+ t.Run(key, func(t *testing.T) {
+ var f map[string]json.RawMessage
+ json.Unmarshal(raw, &f)
+ var str string
+ if json.Unmarshal(value, &str) == nil {
+ f[key], _ = json.Marshal(str + "x")
+ } else {
+ f[key] = json.RawMessage("1")
+ }
+ bad, _ := json.Marshal(f)
+ if _, err := DecodeLaunch(bad); err == nil {
+ t.Fatal("changed binding accepted")
+ }
+ })
+ }
+ for _, mutate := range []func(*ClosedCheckpoint){func(p *ClosedCheckpoint) { p.PreparationID = strings.Repeat("A", 43) }, func(p *ClosedCheckpoint) { p.MainLength-- }, func(p *ClosedCheckpoint) { p.MainSHA256 = strings.Repeat("a", 64) }, func(p *ClosedCheckpoint) { p.CompletedAt = "2026-09-11T19:59:58Z" }} {
+ _, v := launchFixture(t)
+ mutate(v.ClosedCheckpoint)
+ b, _ := json.Marshal(v)
+ if _, err := DecodeLaunch(b); err == nil {
+ t.Fatal("changed preparation accepted")
+ }
+ }
+}
+func TestLaunchSizeUTF8AndSurrogates(t *testing.T) {
+ _, c := launchFixture(t)
+ c.Version = 1
+ c.ExecutionKind = ""
+ c.OperationID = ""
+ c.ExpectedStateSHA256 = ""
+ c.ClosedCheckpoint = nil
+ raw, err := EncodeLaunch(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ exact := append(bytes.Repeat([]byte(" "), MaxLaunchBytes-len(raw)), raw...)
+ if _, err := DecodeLaunch(exact); err != nil {
+ t.Fatal("exact bound refused", err)
+ }
+ r := bytes.NewReader(append(exact, []byte("xx")...))
+ if _, err := ReadLaunch(r); err == nil || r.Len() != 1 {
+ t.Fatal("overflow bound", err, r.Len())
+ }
+ for _, path := range []string{`"/bad\ud800"`, `"/bad\udc00"`, `"/bad\ud800\u0001"`, `"/bad` + string([]byte{0xff}) + `"`} {
+ var f map[string]json.RawMessage
+ json.Unmarshal(raw, &f)
+ f["database_path"] = json.RawMessage(path)
+ bad, e := json.Marshal(f)
+ if e != nil {
+ continue
+ }
+ if _, err := DecodeLaunch(bad); err == nil {
+ t.Fatal("invalid string accepted")
+ }
+ }
+ c.DatabasePath = "/valid/😀/�/quoted\".db"
+ raw, err = EncodeLaunch(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ raw = bytes.ReplaceAll(raw, []byte("😀"), []byte(`\ud83d\ude00`))
+ if _, err := DecodeLaunch(raw); err != nil {
+ t.Fatal("valid surrogate pair refused", err)
+ }
+ if _, err := ReadLaunch(io.MultiReader(bytes.NewReader(raw), errReader{})); err == nil {
+ t.Fatal("read error accepted")
+ }
+}
+
+type errReader struct{}
+
+func (errReader) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF }
diff --git a/internal/maintenance/launch_utf8.go b/internal/maintenance/launch_utf8.go
new file mode 100644
index 000000000..ba1cdb955
--- /dev/null
+++ b/internal/maintenance/launch_utf8.go
@@ -0,0 +1,48 @@
+package maintenance
+
+import "strconv"
+
+// encoding/json replaces unpaired UTF-16 escapes with U+FFFD. Refuse that
+// lossy conversion before decoding identities and paths.
+func validSurrogates(raw []byte) bool {
+ quoted := false
+ for i := 0; i < len(raw); i++ {
+ if raw[i] == '"' {
+ quoted = !quoted
+ continue
+ }
+ if !quoted || raw[i] != '\\' {
+ continue
+ }
+ i++
+ if i >= len(raw) {
+ return false
+ }
+ if raw[i] != 'u' {
+ continue
+ }
+ if i+4 >= len(raw) {
+ return false
+ }
+ n, err := strconv.ParseUint(string(raw[i+1:i+5]), 16, 16)
+ if err != nil {
+ return false
+ }
+ i += 4
+ if n >= 0xdc00 && n <= 0xdfff {
+ return false
+ }
+ if n < 0xd800 || n > 0xdbff {
+ continue
+ }
+ if i+6 >= len(raw) || raw[i+1] != '\\' || raw[i+2] != 'u' {
+ return false
+ }
+ low, err := strconv.ParseUint(string(raw[i+3:i+7]), 16, 16)
+ if err != nil || low < 0xdc00 || low > 0xdfff {
+ return false
+ }
+ i += 6
+ }
+ return true
+}
diff --git a/internal/maintenance/testdata/launch-v2-binding.json b/internal/maintenance/testdata/launch-v2-binding.json
new file mode 100644
index 000000000..5b09c62b4
--- /dev/null
+++ b/internal/maintenance/testdata/launch-v2-binding.json
@@ -0,0 +1 @@
+["carlos/closed-checkpoint/v1","bds","home","overnight-home.bds.oncarlos.com","AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM","AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE","AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI","1111111111111111111111111111111111111111111111111111111111111111","2222222222222222222222222222222222222222222222222222222222222222","3333333333333333333333333333333333333333333333333333333333333333","4444444444444444444444444444444444444444444444444444444444444444",9223372036854775807,"5555555555555555555555555555555555555555555555555555555555555555","6666666666666666666666666666666666666666666666666666666666666666","/data/\u003chome\u003e\u0026/☃.db",18446744073709551615,9007199254740993,"2026-09-11T20:00:00.123456789Z","BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ","2026-09-11T19:59:59.123456789Z",268435456,"7777777777777777777777777777777777777777777777777777777777777777"]
\ No newline at end of file
diff --git a/internal/maintenance/testdata/launch-v2.json b/internal/maintenance/testdata/launch-v2.json
new file mode 100644
index 000000000..39fd33ff7
--- /dev/null
+++ b/internal/maintenance/testdata/launch-v2.json
@@ -0,0 +1,29 @@
+{
+ "version": 2,
+ "account": "bds",
+ "app": "home",
+ "host": "overnight-home.bds.oncarlos.com",
+ "request_id": "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE",
+ "attempt_id": "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI",
+ "request_digest": "1111111111111111111111111111111111111111111111111111111111111111",
+ "record_identity": "2222222222222222222222222222222222222222222222222222222222222222",
+ "artifact_digest": "3333333333333333333333333333333333333333333333333333333333333333",
+ "environment_envelope_digest": "4444444444444444444444444444444444444444444444444444444444444444",
+ "guard_generation": 9223372036854775807,
+ "input_sha256": "5555555555555555555555555555555555555555555555555555555555555555",
+ "database_path": "/data/<home>&/☃.db",
+ "database_device": 18446744073709551615,
+ "database_inode": 9007199254740993,
+ "execution_deadline": "2026-09-11T20:00:00.123456789Z",
+ "execution_kind": "compatibility-check",
+ "operation_id": "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM",
+ "expected_state_sha256": "6666666666666666666666666666666666666666666666666666666666666666",
+ "closed_checkpoint": {
+ "version": 1,
+ "preparation_id": "BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ",
+ "completed_at": "2026-09-11T19:59:59.123456789Z",
+ "main_length": 268435456,
+ "main_sha256": "7777777777777777777777777777777777777777777777777777777777777777",
+ "evidence_sha256": "3dd1eda296f15c602461c856bd344fb4dacc07780c6bfd33f9f756c6a6c34dda"
+ }
+}