Introduce a dormant MutationEnvelope beside the current closure token. Server, Agent, and helper now agree on length-prefixed manifest and authorization bytes, with one shared fixture pinning the hash, key identity, and Ed25519 signature.\n\nExecutor-affecting provenance, location, target, backend, and backend payload now have a signed home before any runtime path migrates. Exact duplicates stay visible in the hash; ordering does not. Current APT/DNF and Windows behavior remains untouched.
126 lines
3.4 KiB
Markdown
126 lines
3.4 KiB
Markdown
# Mutation manifest wire contract
|
|
|
|
This directory owns the language-independent bytes shared by RedFlag Server, Agent, and
|
|
helper. The current closure-based capability token remains the only runtime execution path;
|
|
the mutation envelope is pinned and tested but not wired to a backend yet.
|
|
|
|
## Envelope
|
|
|
|
```text
|
|
MutationEnvelope
|
|
├── MutationManifest
|
|
│ ├── protocol_version
|
|
│ ├── operation_id
|
|
│ ├── target_id
|
|
│ ├── backend
|
|
│ ├── operation
|
|
│ ├── resolved_actions[]
|
|
│ │ ├── kind
|
|
│ │ ├── identity
|
|
│ │ └── payload exact backend-owned UTF-8 JSON bytes
|
|
│ └── evidence[]
|
|
│ ├── kind
|
|
│ └── digest SHA-256 hex
|
|
└── MutationAuthorization
|
|
├── protocol_version
|
|
├── authorization_id
|
|
├── manifest_hash
|
|
├── authority_kind
|
|
├── authority_id
|
|
├── target_id
|
|
├── issued_at / not_before / expires_at
|
|
├── decision
|
|
├── key_id
|
|
└── signature
|
|
```
|
|
|
|
The protocol envelope begins at format `1` in its own namespace. It is not a new numbered
|
|
generation of `CapabilityToken`; the two named contracts coexist until a backend migrates.
|
|
Unknown format values fail closed.
|
|
|
|
## Canonical records
|
|
|
|
Every value is encoded as UTF-8 `decimal_byte_length:value`, with no separator between
|
|
fields. Each record begins with a length-prefixed domain string.
|
|
|
|
Manifest field order:
|
|
|
|
```text
|
|
redflag.mutation-manifest
|
|
protocol_version
|
|
operation_id
|
|
target_id
|
|
backend
|
|
operation
|
|
resolved_action_count
|
|
sorted(length-prefixed resolved-action records)
|
|
evidence_count
|
|
sorted(length-prefixed evidence records)
|
|
```
|
|
|
|
Resolved action field order:
|
|
|
|
```text
|
|
redflag.resolved-action
|
|
kind
|
|
identity
|
|
payload
|
|
```
|
|
|
|
Evidence field order:
|
|
|
|
```text
|
|
redflag.evidence
|
|
kind
|
|
digest
|
|
```
|
|
|
|
Authorization field order:
|
|
|
|
```text
|
|
redflag.mutation-authorization
|
|
protocol_version
|
|
authorization_id
|
|
manifest_hash
|
|
authority_kind
|
|
authority_id
|
|
target_id
|
|
issued_at
|
|
not_before
|
|
expires_at
|
|
decision
|
|
key_id
|
|
```
|
|
|
|
`manifest_hash` is lowercase hex SHA-256 over the manifest canonical bytes. The Ed25519
|
|
signature is over the authorization canonical bytes. The signature itself is not included
|
|
in those bytes; every other authorization field, including `key_id`, is.
|
|
|
|
## Collection semantics
|
|
|
|
Resolved actions and evidence are unordered multisets. Their canonical records are sorted
|
|
bytewise for hashing. Exact duplicates remain present, increment the count, and change the
|
|
hash. No implementation may convert either collection to a set.
|
|
|
|
A backend that needs ordered execution encodes the sequence inside one signed payload. The
|
|
common layer never infers package, WUA, Winget, Docker, or self-update semantics from that
|
|
payload.
|
|
|
|
Provenance is evidence. Cache paths, URLs, repository selectors, WUA identities, and other
|
|
execution inputs belong in the exact signed backend payload or must be derived
|
|
deterministically from it.
|
|
|
|
## Golden fixture
|
|
|
|
`testdata/mutation-golden.json` pins:
|
|
|
|
- manifest canonical bytes and hash;
|
|
- authorization canonical bytes;
|
|
- authority key ID;
|
|
- deterministic Ed25519 signature;
|
|
- action/evidence ordering behavior;
|
|
- duplicate retention;
|
|
- tamper refusal for provenance, execution location, target, backend, resolved action,
|
|
authorization metadata, decision, time window, and unknown formats.
|
|
|
|
Go tests live in both capability packages. Rust tests live in `helper/src/mutation_protocol.rs`.
|