113 lines
4.9 KiB
Text
113 lines
4.9 KiB
Text
# schemas/lib/op.ncl — Op (Operation) governance contracts
|
|
#
|
|
# An Op is the atomic unit of workspace state management — it records intent,
|
|
# authorization, execution artifacts, and state transitions as a DAG node,
|
|
# enabling audit, rollback, and concurrent agent control.
|
|
#
|
|
# Storage (per op):
|
|
# ops/{id}/op.json — runtime instance record (JSON, not NCL)
|
|
# ops/{id}/pre.json — pre-execution state snapshot
|
|
# ops/{id}/post.json — post-execution state snapshot (absent on failure)
|
|
# .ops-archive/ — restic repo (S3 backend): logs + bundles, encrypted
|
|
#
|
|
# Identity:
|
|
# actor.nid = Radicle Node ID (rad self --nid). Falls back to "local:{user}".
|
|
# Op ID = {nid-short}:{uuid} — globally attributable, DAG-safe
|
|
#
|
|
# DAG semantics:
|
|
# Each Op is a node. lineage.parent_op is the incoming edge.
|
|
# Rollback is a new forward Op — lineage.rollback_of points to the Op being undone.
|
|
# The DAG remains acyclic; rollback is a forward move restoring an earlier snapshot.
|
|
|
|
let _OpActor = {
|
|
nid | String | doc "Radicle Node ID (rad self --nid) or 'local:{user}' fallback",
|
|
identity | String | doc "Human-readable label — username or agent name",
|
|
source | [| 'cli, 'agent, 'api |] | default = 'cli,
|
|
} in
|
|
|
|
let _Constraint = {
|
|
kind | [| 'backup, 'restore, 'health_check, 'dry_run_check, 'concurrent_lock |],
|
|
resource | String | doc "Component name or volume/resource identifier",
|
|
scope | [| 'direct, 'indirect |] | default = 'direct,
|
|
params | { .. } | default = {},
|
|
} in
|
|
|
|
let _RecoveryAction = {
|
|
kind | [| 'backup, 'restore, 'health_check, 'dry_run_check, 'concurrent_lock |],
|
|
resource | String,
|
|
from | [| 'pre_backup, 'last_known_good |] | default = 'pre_backup,
|
|
params | { .. } | default = {},
|
|
} in
|
|
|
|
let _OpConstraints = {
|
|
pre | Array _Constraint | doc "Gates evaluated before execution starts" | default = [],
|
|
on_failure | Array _RecoveryAction | doc "Recovery actions if op fails" | default = [],
|
|
} in
|
|
|
|
let _OpSnapshots = {
|
|
pre | String | doc "Relative path to pre.json from workspace root",
|
|
post | String | optional | doc "Relative path to post.json — absent if op failed before completion",
|
|
} in
|
|
|
|
let _OpArtifacts = {
|
|
archive_snapshot | String | optional | doc "Snapshot ID in the configured archive backend (restic/kopia)",
|
|
bundles | Array String | doc "Bundle tar.gz paths within the archive snapshot" | default = [],
|
|
} in
|
|
|
|
let _OpLineage = {
|
|
parent_op | String | optional | doc "Op ID this state was derived from (incoming DAG edge)",
|
|
rollback_of | String | optional | doc "If this op is a rollback, the ID of the op it undoes",
|
|
} in
|
|
|
|
{
|
|
OpActor = _OpActor,
|
|
Constraint = _Constraint,
|
|
RecoveryAction = _RecoveryAction,
|
|
OpConstraints = _OpConstraints,
|
|
OpSnapshots = _OpSnapshots,
|
|
OpArtifacts = _OpArtifacts,
|
|
OpLineage = _OpLineage,
|
|
|
|
# Enum type exports — use these as field annotation values in workspace NCL
|
|
OpSource = [| 'cli, 'agent, 'api |],
|
|
OpOperation = [| 'install, 'update, 'delete, 'rollback, 'dry_run |],
|
|
OpStatus = [| 'pending, 'running, 'constraint_failed, 'recovering, 'success, 'failed, 'rolled_back, 'cancelled |],
|
|
|
|
Op = {
|
|
id | String | doc "Op ID: {nid-short}:{uuid}",
|
|
actor | _OpActor,
|
|
intent | String | doc "Human description of why this op is needed",
|
|
workspace | String | doc "Workspace name from config/provisioning.ncl",
|
|
component | String | doc "Component being operated on",
|
|
operation | [| 'install, 'update, 'delete, 'rollback, 'dry_run |],
|
|
targets | Array String | doc "Server hostnames targeted by this op",
|
|
|
|
constraints | _OpConstraints | default = { pre = [], on_failure = [] },
|
|
snapshots | _OpSnapshots,
|
|
artifacts | _OpArtifacts | default = { bundles = [] },
|
|
lineage | _OpLineage | default = {},
|
|
|
|
status | [| 'pending, 'running, 'constraint_failed, 'recovering, 'success, 'failed, 'rolled_back, 'cancelled |] | default = 'pending,
|
|
jj_change | String | optional | doc "jj change ID created for this op",
|
|
radicle_rid | String | optional | doc "Radicle Repository ID after rad sync — globally unique RID of this workspace",
|
|
|
|
started_at | String | doc "ISO 8601 UTC timestamp",
|
|
ended_at | String | optional,
|
|
},
|
|
|
|
# Workspace-level ops configuration — added under `ops` in config/provisioning.ncl
|
|
OpsConfig = {
|
|
archive = {
|
|
backend | [| 's3, 'local |] | default = 's3,
|
|
tool | [| 'restic, 'kopia |] | doc "Backup provider — must have a matching entry in extensions/providers/backup/" | default = 'restic,
|
|
endpoint | String | optional | doc "S3-compatible endpoint URL",
|
|
bucket | String | optional,
|
|
prefix | String | default = "ops",
|
|
},
|
|
retention = {
|
|
keep_last | Number | default = 50,
|
|
keep_monthly | Number | default = 12,
|
|
keep_yearly | Number | default = 3,
|
|
},
|
|
},
|
|
}
|