@synoi/gap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +195 -0
- package/README.md +223 -0
- package/dist/canonicalize.d.ts +19 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +36 -0
- package/dist/canonicalize.js.map +1 -0
- package/dist/capabilities.d.ts +605 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +53 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cdro.d.ts +63 -0
- package/dist/cdro.d.ts.map +1 -0
- package/dist/cdro.js +16 -0
- package/dist/cdro.js.map +1 -0
- package/dist/channels.d.ts +107 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +29 -0
- package/dist/channels.js.map +1 -0
- package/dist/constants.d.ts +32 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +36 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/oid.d.ts +28 -0
- package/dist/oid.d.ts.map +1 -0
- package/dist/oid.js +68 -0
- package/dist/oid.js.map +1 -0
- package/dist/receipts.d.ts +128 -0
- package/dist/receipts.d.ts.map +1 -0
- package/dist/receipts.js +14 -0
- package/dist/receipts.js.map +1 -0
- package/dist/revocations.d.ts +65 -0
- package/dist/revocations.d.ts.map +1 -0
- package/dist/revocations.js +22 -0
- package/dist/revocations.js.map +1 -0
- package/dist/validate.d.ts +59 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +835 -0
- package/dist/validate.js.map +1 -0
- package/dist/workflows.d.ts +186 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +14 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +55 -0
- package/src/canonicalize.ts +38 -0
- package/src/capabilities.ts +711 -0
- package/src/cdro.ts +92 -0
- package/src/channels.ts +183 -0
- package/src/constants.ts +46 -0
- package/src/index.ts +180 -0
- package/src/oid.ts +71 -0
- package/src/receipts.ts +169 -0
- package/src/revocations.ts +90 -0
- package/src/validate.ts +1008 -0
- package/src/workflows.ts +241 -0
package/src/workflows.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflows.ts -- WorkflowDefinition / WorkflowInstance / StageTransition.
|
|
3
|
+
*
|
|
4
|
+
* Workflows choreograph the HITL flow when a capability invocation requires
|
|
5
|
+
* human approval (or any orchestrated multi-stage interaction). Shapes
|
|
6
|
+
* mirror the GAP wire types.
|
|
7
|
+
*
|
|
8
|
+
* State model:
|
|
9
|
+
* - WorkflowDefinition is a static template.
|
|
10
|
+
* - WorkflowInstance is a single live run.
|
|
11
|
+
* - StageTransition is the audited edge between two stages of an instance.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { GapCdroEnvelope } from './cdro.js'
|
|
15
|
+
import type { CapabilityPredicate } from './capabilities.js'
|
|
16
|
+
import type {
|
|
17
|
+
ChannelKind,
|
|
18
|
+
StageAction,
|
|
19
|
+
StageListen,
|
|
20
|
+
StageTransitionTarget,
|
|
21
|
+
} from './channels.js'
|
|
22
|
+
|
|
23
|
+
// -- Optional ambient effect -------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optional ambient effect -- fires if the operator's environment has a
|
|
27
|
+
* matching declared+granted capability, silently skipped if not. See
|
|
28
|
+
* OPTIONAL_CAPABILITIES_SPEC.md for the full design.
|
|
29
|
+
*
|
|
30
|
+
* The match algorithm:
|
|
31
|
+
* 1. Resolve `requires_capability` against gap:capability_declaration
|
|
32
|
+
* records that are (a) not revoked, (b) granted to this workflow's
|
|
33
|
+
* invoking actor, (c) visible per the operator's sharing policy.
|
|
34
|
+
* 2. If zero matches -> skip silently.
|
|
35
|
+
* 3. If >=1 match -> fire the action through the named channel.
|
|
36
|
+
* 4. Failures of optional_effects DO NOT propagate to stage outcome.
|
|
37
|
+
* They're best-effort by definition.
|
|
38
|
+
*/
|
|
39
|
+
export interface OptionalEffect {
|
|
40
|
+
/** Capability the effect needs in the environment to fire. Supports
|
|
41
|
+
dotted-taxonomy wildcards: 'home.lighting.*' matches any lighting
|
|
42
|
+
capability the operator's smart home has declared. */
|
|
43
|
+
requires_capability: string
|
|
44
|
+
/** The action to perform when the capability is matched. */
|
|
45
|
+
action: StageAction
|
|
46
|
+
/** Optional human-readable label for audit logs + portal UI. */
|
|
47
|
+
label?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// -- Stage safety wrapper (CI/CD governance net) -----------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* CI/CD safety net (see SYNOI_CICD_GOVERNANCE_SPEC.md §10.5). When set,
|
|
54
|
+
* the engine routes the stage's invocation through runSafetyPipeline:
|
|
55
|
+
* snapshot -> two-person -> cooldown -> invoke -> record.
|
|
56
|
+
* All four sub-pieces are independently optional. Absent => default
|
|
57
|
+
* behaviour (no safety wrapping; existing GAP semantics).
|
|
58
|
+
*/
|
|
59
|
+
export interface StageSafety {
|
|
60
|
+
/** Marker -- set when this stage performs a destructive operation.
|
|
61
|
+
* Required for the safety wrapper to engage. */
|
|
62
|
+
destructive: true
|
|
63
|
+
/** Resource kind for snapshot routing. Engine picks the adapter by kind. */
|
|
64
|
+
resource_kind?:
|
|
65
|
+
| 'postgres_db'
|
|
66
|
+
| 'mysql_db'
|
|
67
|
+
| 'aws_rds_instance'
|
|
68
|
+
| 'aws_ebs_volume'
|
|
69
|
+
| 'aws_s3_object_versioned'
|
|
70
|
+
| 'k8s_namespace_velero'
|
|
71
|
+
| 'terraform_state'
|
|
72
|
+
| 'git_branch'
|
|
73
|
+
| 'filesystem_path'
|
|
74
|
+
/** Override adapter name. If unset, engine picks the first registered
|
|
75
|
+
* adapter for resource_kind. */
|
|
76
|
+
snapshot_adapter?: string
|
|
77
|
+
/** Resource id passed to adapter.capture() -- usually interpolated. */
|
|
78
|
+
snapshot_resource_id?: string
|
|
79
|
+
/** Adapter config (interpolated). */
|
|
80
|
+
snapshot_config?: Record<string, string>
|
|
81
|
+
/** Two-person rule config. Pass null/undefined to skip. */
|
|
82
|
+
two_person?: {
|
|
83
|
+
required: number
|
|
84
|
+
require_disjoint_groups?: boolean
|
|
85
|
+
eligible_groups?: string[]
|
|
86
|
+
}
|
|
87
|
+
/** Cooldown window in milliseconds. 0/undefined => skip. */
|
|
88
|
+
cooldown_ms?: number
|
|
89
|
+
/** Approver-groups that can cancel the cooldown. */
|
|
90
|
+
cooldown_eligible_groups?: string[]
|
|
91
|
+
/** Optional OID of the paired rollback workflow. */
|
|
92
|
+
rollback_workflow_oid?: string
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// -- Stage shape -------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
export interface WorkflowStage {
|
|
98
|
+
stage_id: string
|
|
99
|
+
duration_seconds?: number
|
|
100
|
+
actions?: StageAction[]
|
|
101
|
+
/** Optional ambient effects -- fire if matching capabilities exist; skip
|
|
102
|
+
silently otherwise. Independent of the mandatory `actions` list. */
|
|
103
|
+
optional_effects?: OptionalEffect[]
|
|
104
|
+
/**
|
|
105
|
+
* Actor OIDs permitted to supply valid approval signals for this stage.
|
|
106
|
+
* When set, the gateway MUST verify that the approver's authenticated identity
|
|
107
|
+
* resolves to one of these OIDs before advancing the stage on a YES signal.
|
|
108
|
+
* The gateway MUST enforce actor_oid disjointness: the same actor_oid cannot
|
|
109
|
+
* count as more than one approval (prevents one person approving twice).
|
|
110
|
+
*
|
|
111
|
+
* MUST be set for stages governing physical_safety=true or safety_class C
|
|
112
|
+
* capabilities. Absent means any tenant-authenticated signal is accepted
|
|
113
|
+
* (dangerous; use only for class A/B capabilities).
|
|
114
|
+
*/
|
|
115
|
+
authorized_approvers?: string[]
|
|
116
|
+
listen?: StageListen[]
|
|
117
|
+
/**
|
|
118
|
+
* Stage to transition to when the stage timer expires.
|
|
119
|
+
*
|
|
120
|
+
* WARNING: For workflow definitions whose trigger matches a
|
|
121
|
+
* physical_safety=true or safety_class C capability, `on_timeout` MUST NOT
|
|
122
|
+
* lead to a terminal stage with `terminal_outcome='approved'`. The gateway
|
|
123
|
+
* MUST reject such definitions at registration time (PC-11).
|
|
124
|
+
*/
|
|
125
|
+
on_timeout?: StageTransitionTarget
|
|
126
|
+
on_action_failure?: StageTransitionTarget
|
|
127
|
+
terminal?: boolean
|
|
128
|
+
terminal_outcome?: 'approved' | 'denied' | 'timed_out' | 'withdrawn' | 'error'
|
|
129
|
+
invocation?: {
|
|
130
|
+
capability: string
|
|
131
|
+
args: Record<string, unknown>
|
|
132
|
+
on_success?: StageTransitionTarget
|
|
133
|
+
on_failure?: StageTransitionTarget
|
|
134
|
+
}
|
|
135
|
+
precondition?: CapabilityPredicate
|
|
136
|
+
safety?: StageSafety
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Alias used in the spec docs + spec discussion. WorkflowStage is the
|
|
140
|
+
* authoritative shape; this alias makes the engine API readable. */
|
|
141
|
+
export type WorkflowStageDefinition = WorkflowStage
|
|
142
|
+
|
|
143
|
+
// -- Triggers ----------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
export type WorkflowTriggerKind =
|
|
146
|
+
| 'risk_policy'
|
|
147
|
+
| 'capability_invocation'
|
|
148
|
+
| 'explicit'
|
|
149
|
+
| 'schedule'
|
|
150
|
+
|
|
151
|
+
export interface WorkflowTrigger {
|
|
152
|
+
kind: WorkflowTriggerKind
|
|
153
|
+
risk_class?: 'A' | 'B' | 'C'
|
|
154
|
+
action_class?: string
|
|
155
|
+
action_type_pattern?: string
|
|
156
|
+
capability_pattern?: string
|
|
157
|
+
cron?: string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// -- Definition CDRO ---------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
export interface WorkflowDefinitionBody {
|
|
163
|
+
workflow_id: string
|
|
164
|
+
workflow_name: string
|
|
165
|
+
workflow_version: string
|
|
166
|
+
description?: string
|
|
167
|
+
trigger: WorkflowTrigger
|
|
168
|
+
stages: WorkflowStage[]
|
|
169
|
+
initial_stage_id: string
|
|
170
|
+
cleanup_stage_id?: string
|
|
171
|
+
required_channels: ChannelKind[]
|
|
172
|
+
optional_channels?: ChannelKind[]
|
|
173
|
+
max_total_duration_seconds: number
|
|
174
|
+
/**
|
|
175
|
+
* When true, this workflow definition (and any supersession of it) requires
|
|
176
|
+
* operator-level authorization before it becomes active. The registration
|
|
177
|
+
* request must carry the operator's signed attestation. MUST be true for
|
|
178
|
+
* definitions whose `trigger.capability_pattern` matches any
|
|
179
|
+
* physical_safety=true capability. The gateway MUST reject registration of
|
|
180
|
+
* unsafe (on_timeout->approved) paths for physical safety patterns.
|
|
181
|
+
*/
|
|
182
|
+
requires_operator_approval?: boolean
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export type WorkflowDefinition = GapCdroEnvelope<WorkflowDefinitionBody>
|
|
186
|
+
|
|
187
|
+
// -- Instance CDRO -----------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
export interface WorkflowInstanceBody {
|
|
190
|
+
workflow_definition_oid: string
|
|
191
|
+
workflow_id: string
|
|
192
|
+
trigger_event: {
|
|
193
|
+
kind: WorkflowTriggerKind
|
|
194
|
+
source_invocation_oid?: string
|
|
195
|
+
source_risk_policy_id?: string
|
|
196
|
+
source_actor_oid: string
|
|
197
|
+
}
|
|
198
|
+
current_stage_id: string
|
|
199
|
+
scope_variables: Record<string, unknown>
|
|
200
|
+
started_at_ms: number
|
|
201
|
+
last_transition_at_ms: number
|
|
202
|
+
terminated_at_ms: number | null
|
|
203
|
+
terminal_outcome: WorkflowStage['terminal_outcome'] | null
|
|
204
|
+
active_channel_listeners: Array<{
|
|
205
|
+
channel: ChannelKind
|
|
206
|
+
listen_spec: StageListen
|
|
207
|
+
started_at_ms: number
|
|
208
|
+
}>
|
|
209
|
+
transition_oids: string[]
|
|
210
|
+
final_receipt_oid?: string
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export type WorkflowInstance = GapCdroEnvelope<WorkflowInstanceBody>
|
|
214
|
+
|
|
215
|
+
// -- Stage transition CDRO ---------------------------------------------------
|
|
216
|
+
|
|
217
|
+
export type StageTransitionReason =
|
|
218
|
+
| 'listen_matched'
|
|
219
|
+
| 'timeout'
|
|
220
|
+
| 'action_completed'
|
|
221
|
+
| 'action_failed'
|
|
222
|
+
| 'precondition_passed'
|
|
223
|
+
| 'precondition_failed'
|
|
224
|
+
| 'invocation_succeeded'
|
|
225
|
+
| 'invocation_failed'
|
|
226
|
+
| 'external_signal'
|
|
227
|
+
| 'cleanup'
|
|
228
|
+
|
|
229
|
+
export interface StageTransitionBody {
|
|
230
|
+
workflow_instance_oid: string
|
|
231
|
+
previous_transition_oid: string | null
|
|
232
|
+
from_stage_id: string
|
|
233
|
+
to_stage_id: string
|
|
234
|
+
trigger_reason: StageTransitionReason
|
|
235
|
+
bind_outputs: Record<string, unknown>
|
|
236
|
+
triggering_event_oid?: string
|
|
237
|
+
triggering_invocation_oid?: string
|
|
238
|
+
transitioned_at_ms: number
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export type StageTransition = GapCdroEnvelope<StageTransitionBody>
|