Initialize repository snapshot
This commit is contained in:
22
spec/sentinel/README.md
Normal file
22
spec/sentinel/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Sentinel v1 Schemas (v1)
|
||||
|
||||
This directory contains implementer-facing, machine-checkable schemas and deterministic verification rules for Sentinel v1.
|
||||
|
||||
- `canonicalization.md`: normative hashing + Merkle + root publication rules.
|
||||
- `event.schema.json`: minimal event schema for `receipts*.jsonl` export.
|
||||
- `seal.schema.json`: minimal schema for `seal.json` inside an Ouroboros seal bundle.
|
||||
- `integrity.schema.json`: optional schema for `integrity.json` (hashes of bundle files).
|
||||
- `verifier_manifest.schema.json`: optional schema for `verifier_manifest.json` (tool/version expectations).
|
||||
|
||||
Related v1 documents:
|
||||
- `spec/SENTINEL_OFFLINE_VERIFIER_REQUIREMENTS.md`
|
||||
- `spec/SENTINEL_V1_CONTRACT_MATRIX.md`
|
||||
|
||||
Reference verifier + testvector:
|
||||
- Verifier: `tools/vm_verify_sentinel_bundle.py`
|
||||
- Testvector bundle: `testvectors/sentinel/black-box-that-refused/`
|
||||
|
||||
Quick run:
|
||||
```bash
|
||||
python3 tools/vm_verify_sentinel_bundle.py --bundle testvectors/sentinel/black-box-that-refused --strict
|
||||
```
|
||||
123
spec/sentinel/canonicalization.md
Normal file
123
spec/sentinel/canonicalization.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Sentinel v1 Canonicalization & Hashing Rules
|
||||
|
||||
This document defines deterministic event hashing and Merkle root computation for Sentinel v1. Verification MUST be deterministic across platforms given the same artifacts.
|
||||
|
||||
## 1) Hash function (`hash_algo`)
|
||||
|
||||
`hash_algo` MUST be one of:
|
||||
- `blake3` (recommended)
|
||||
- `sha256` (fallback for constrained platforms)
|
||||
|
||||
The chosen `hash_algo` MUST be constant for a given Sentinel instance/build. Verifiers MUST reject mixed algorithms within a single bundle unless explicitly versioned.
|
||||
|
||||
### 1.1 `vmhash`
|
||||
|
||||
`vmhash(data: bytes) -> string` returns:
|
||||
- `"blake3:" + hex(blake3(data))` when `hash_algo=blake3`
|
||||
- `"sha256:" + hex(sha256(data))` when `hash_algo=sha256`
|
||||
|
||||
`hex(...)` is lowercase hex with no separators.
|
||||
|
||||
## 2) JSON canonicalization (`canonicalization_version`)
|
||||
|
||||
`canonicalization_version` for Sentinel v1 events is:
|
||||
|
||||
- `sentinel-event-jcs-v1`
|
||||
|
||||
Canonical JSON MUST use RFC 8785 (JSON Canonicalization Scheme, “JCS”):
|
||||
- UTF-8 encoding
|
||||
- Object keys sorted lexicographically
|
||||
- No insignificant whitespace
|
||||
- Numbers encoded per JCS rules
|
||||
|
||||
If a platform cannot implement full JCS, it MUST NOT claim `sentinel-event-jcs-v1`.
|
||||
|
||||
## 3) Event canonical bytes
|
||||
|
||||
Each exported event is a JSON object that conforms to `event.schema.json`.
|
||||
|
||||
`event_canonical_bytes` is the UTF-8 bytes of the JCS-canonicalized event object.
|
||||
|
||||
## 4) Event hash + hash chain
|
||||
|
||||
### 4.1 `event_hash`
|
||||
|
||||
`event_hash` MUST be computed over the canonical bytes of the event object *excluding* the `event_hash` field itself.
|
||||
|
||||
Define:
|
||||
- `event_without_event_hash = event` with the `event_hash` property removed (if present)
|
||||
- `event_canonical_bytes = jcs_bytes(event_without_event_hash)`
|
||||
|
||||
Then:
|
||||
|
||||
`event_hash = vmhash(event_canonical_bytes)`
|
||||
|
||||
For exported artifacts, `event_hash` MUST be present in the event record and verifiers MUST recompute and compare it.
|
||||
|
||||
### 4.2 `prev_event_hash`
|
||||
|
||||
- For `seq = 0` (or the first event in a new ledger): `prev_event_hash = "0"`
|
||||
- For `seq = n > 0`: `prev_event_hash` MUST equal the computed `event_hash` of the immediately preceding event (`seq = n-1`) in the same ledger.
|
||||
|
||||
This provides fast tamper evidence even without Merkle recomputation.
|
||||
|
||||
## 5) Operation digest (`op_digest`)
|
||||
|
||||
`op_digest` commits to the *normalized* operation descriptor.
|
||||
|
||||
Define the normalized object:
|
||||
```json
|
||||
{
|
||||
"op": "<op>",
|
||||
"params": { "canonical": "params" }
|
||||
}
|
||||
```
|
||||
|
||||
Normalization rules:
|
||||
- `op` MUST be a stable, versioned identifier (e.g., `sentinel.export_seal.v1`).
|
||||
- `params` MUST be JSON (no NaN/Infinity); omit unset fields rather than using null where possible.
|
||||
- Canonicalize the object using `sentinel-event-jcs-v1`, then hash:
|
||||
|
||||
`op_digest = vmhash(jcs_bytes({"op": op, "params": params}))`
|
||||
|
||||
## 6) Merkle root (`ROOT.current.txt`)
|
||||
|
||||
### 6.1 Leaves
|
||||
|
||||
The Merkle tree commits to the ordered list of event hashes:
|
||||
|
||||
`leaves = [event_hash(seq=0), event_hash(seq=1), ...]`
|
||||
|
||||
Each leaf is a `vmhash` string (`algo:hex`).
|
||||
|
||||
Note on ranged bundles: A verifier can only recompute the global Merkle roots for an arbitrary `since_seq > 0` bundle if it is also given a verifiable Merkle continuation state (e.g., a frontier snapshot) at `since_seq-1`. Otherwise, verification MUST fall back to hash-chain + file-integrity checks for that range, or the bundle MUST start at `since_seq = 0`.
|
||||
|
||||
### 6.2 Parent computation (VaultMesh-style)
|
||||
|
||||
To compute a parent from two children:
|
||||
- Let `left_hex = left.split(":", 1)[-1]`
|
||||
- Let `right_hex = right.split(":", 1)[-1]`
|
||||
- `parent = vmhash( (left_hex + right_hex).encode("utf-8") )`
|
||||
|
||||
If the level has an odd count, duplicate the last element (i.e., `right = left`).
|
||||
|
||||
### 6.3 Empty tree root
|
||||
|
||||
If there are no leaves, the root MUST be:
|
||||
|
||||
`vmhash(b"empty")`
|
||||
|
||||
### 6.4 Root publication file format
|
||||
|
||||
`ROOT.current.txt` MUST be human-readable and parseable as key/value lines:
|
||||
|
||||
```
|
||||
format=vm-sentinel-root-v1
|
||||
root=<algo:hex>
|
||||
seq=<u64>
|
||||
updated_at=<ISO-8601 Z>
|
||||
hash_algo=<blake3|sha256>
|
||||
canonicalization_version=sentinel-event-jcs-v1
|
||||
```
|
||||
|
||||
Additional keys MAY be included, but verifiers MUST ignore unknown keys.
|
||||
68
spec/sentinel/event.schema.json
Normal file
68
spec/sentinel/event.schema.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "VaultMesh Sentinel v1 Event",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"event_id",
|
||||
"seq",
|
||||
"ts",
|
||||
"event_type",
|
||||
"actor",
|
||||
"cap_hash",
|
||||
"op",
|
||||
"op_digest",
|
||||
"result",
|
||||
"trace_id",
|
||||
"prev_event_hash",
|
||||
"event_hash",
|
||||
"payload"
|
||||
],
|
||||
"properties": {
|
||||
"event_id": { "type": "string", "format": "uuid" },
|
||||
"seq": { "type": "integer", "minimum": 0 },
|
||||
"ts": {
|
||||
"description": "Monotonic + wallclock if available. Accepts ISO-8601 Z or a structured object.",
|
||||
"anyOf": [
|
||||
{ "type": "string" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["wall"],
|
||||
"properties": {
|
||||
"wall": { "type": "string", "format": "date-time" },
|
||||
"mono_ns": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"event_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"action_intent",
|
||||
"policy_decision",
|
||||
"action_executed",
|
||||
"shadow_receipt",
|
||||
"cap_grant",
|
||||
"cap_revoke",
|
||||
"seal_created",
|
||||
"root_published",
|
||||
"corruption_detected",
|
||||
"tamper_signal",
|
||||
"boot_event",
|
||||
"health_event"
|
||||
]
|
||||
},
|
||||
"actor": { "type": "string", "minLength": 1 },
|
||||
"cap_hash": { "type": "string", "minLength": 1 },
|
||||
"op": { "type": "string", "minLength": 1 },
|
||||
"op_digest": { "type": "string", "minLength": 1 },
|
||||
"result": { "type": "string", "enum": ["ok", "deny", "error"] },
|
||||
"root_before": { "type": "string" },
|
||||
"root_after": { "type": "string" },
|
||||
"trace_id": { "type": "string", "format": "uuid" },
|
||||
"prev_event_hash": { "type": "string", "minLength": 1 },
|
||||
"event_hash": { "type": "string" },
|
||||
"payload": { "type": "object" }
|
||||
}
|
||||
}
|
||||
24
spec/sentinel/integrity.schema.json
Normal file
24
spec/sentinel/integrity.schema.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "VaultMesh Sentinel v1 Integrity Manifest (integrity.json)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["format", "hash_algo", "files"],
|
||||
"properties": {
|
||||
"format": { "type": "string", "const": "vm-sentinel-integrity-v1" },
|
||||
"hash_algo": { "type": "string", "enum": ["blake3", "sha256"] },
|
||||
"files": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["path", "digest"],
|
||||
"properties": {
|
||||
"path": { "type": "string" },
|
||||
"digest": { "type": "string" },
|
||||
"size_bytes": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
spec/sentinel/seal.schema.json
Normal file
62
spec/sentinel/seal.schema.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "VaultMesh Sentinel v1 Seal Bundle (seal.json)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"format",
|
||||
"sentinel_version",
|
||||
"schema_version",
|
||||
"hash_algo",
|
||||
"canonicalization_version",
|
||||
"seal_id",
|
||||
"created_at",
|
||||
"range",
|
||||
"root",
|
||||
"files"
|
||||
],
|
||||
"properties": {
|
||||
"format": { "type": "string", "const": "vm-sentinel-seal-v1" },
|
||||
"sentinel_version": { "type": "string" },
|
||||
"schema_version": { "type": "string" },
|
||||
"hash_algo": { "type": "string", "enum": ["blake3", "sha256"] },
|
||||
"canonicalization_version": { "type": "string" },
|
||||
"seal_id": { "type": "string" },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"instance_id": { "type": "string" },
|
||||
"ledger_type": { "type": "string", "enum": ["sqlite", "jsonl"] },
|
||||
"range": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["since_seq", "until_seq"],
|
||||
"properties": {
|
||||
"since_seq": { "type": "integer", "minimum": 0 },
|
||||
"until_seq": { "type": "integer", "minimum": 0 },
|
||||
"since_ts": { "type": "string" },
|
||||
"until_ts": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["start", "end"],
|
||||
"properties": {
|
||||
"start": { "type": "string" },
|
||||
"end": { "type": "string" },
|
||||
"seq": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["receipts", "roots", "integrity", "verifier_manifest"],
|
||||
"properties": {
|
||||
"receipts": { "type": "string" },
|
||||
"roots": { "type": "string" },
|
||||
"integrity": { "type": "string" },
|
||||
"verifier_manifest": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"notes": { "type": "string" }
|
||||
}
|
||||
}
|
||||
23
spec/sentinel/verifier_manifest.schema.json
Normal file
23
spec/sentinel/verifier_manifest.schema.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "VaultMesh Sentinel v1 Verifier Manifest (verifier_manifest.json)",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["format", "sentinel_version", "schema_version", "canonicalization_version"],
|
||||
"properties": {
|
||||
"format": { "type": "string", "const": "vm-sentinel-verifier-manifest-v1" },
|
||||
"sentinel_version": { "type": "string" },
|
||||
"schema_version": { "type": "string" },
|
||||
"hash_algo": { "type": "string", "enum": ["blake3", "sha256"] },
|
||||
"canonicalization_version": { "type": "string" },
|
||||
"verifier": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"version": { "type": "string" },
|
||||
"sha256": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user