@opnli/atl-devkit 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/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @opnli/atl-devkit
2
+
3
+ **Add human consent to any AI agent in 3 lines.**
4
+
5
+ The Agent Trust Layer (ATL) DevKit gives developers a drop-in consent
6
+ gate for AI agents. When your agent tries to act on the world — read a
7
+ file, call an API, run a command — the ATL holds the action and asks the
8
+ human: **Allow or Block?**
9
+
10
+ The human's decision is final. The audit trail is tamper-evident.
11
+ There is no bypass.
12
+
13
+ ## Quick Start
14
+
15
+ const { createConsentGate } = require('@opnli/atl-devkit');
16
+ const gate = createConsentGate({ onConsent: askTheHuman, timeout: 60000 });
17
+ myProxy.onMessage((msg, raw) => {
18
+ const result = gate.inspect(msg, raw, false);
19
+ if (result.action === 'forward') send(raw);
20
+ // 'hold' means consent requested. Wait for gate.resolve(holdId, 'allow'|'deny')
21
+ });
22
+
23
+ ## Why This Exists
24
+
25
+ AI agents are getting powerful. They can read your files, search the
26
+ web, execute code, and call APIs — often without asking. The industry
27
+ is moving fast on capability. Nobody is moving on trust.
28
+
29
+ The ATL is the trust layer. It does not slow agents down. It puts the
30
+ human in control of what agents do. **Be the enabler for the timid.
31
+ Never be the barrier to the powerful.**
32
+
33
+ ## The Three Realities of Trust
34
+
35
+ Every trust decision involves three realities:
36
+
37
+ 1. **Identity** — Know who is acting. (CARD credentials)
38
+ 2. **Consent** — Choose what is allowed. (This DevKit)
39
+ 3. **Accountability** — Check what happened. (Audit log)
40
+
41
+ The DevKit delivers Consent and Accountability. Identity comes from
42
+ the Opn.li Trust Network (https://opn.li) via CARD credentials.
43
+
44
+ ## Two Shield Levels
45
+
46
+ ### Green Shield — Consent Before Execution
47
+
48
+ The agent requests permission to act. The action has **NOT happened
49
+ yet**. The human decides whether it happens. This requires the agent
50
+ platform to support pre-execution approval events.
51
+
52
+ Agent: "I want to run: npm install express"
53
+ -> ATL HOLDS the request
54
+ -> Human sees: "Your AI wants to run a shell command: npm install express"
55
+ -> Human clicks Allow -> command executes
56
+ -> Human clicks Block -> command never runs
57
+
58
+ ### Yellow Shield — Consent Before Delivery
59
+
60
+ The agent executed an action, but the result has not been delivered to
61
+ the UI. The ATL detects the execution via a sequence gap in the event
62
+ stream and holds the result. The human decides whether to receive it.
63
+
64
+ This works on **any platform** that streams sequential agent events —
65
+ no platform modifications required.
66
+
67
+ Agent executes a tool (seqs 2-4 consumed internally)
68
+ -> ATL detects gap: seq jumped from 1 to 5
69
+ -> ATL HOLDS the result
70
+ -> Human sees: "Your AI executed an action. Allow the result?"
71
+ -> Human clicks Allow -> result delivered
72
+ -> Human clicks Block -> result dropped, agent notified
73
+
74
+ ## API
75
+
76
+ ### createConsentGate(options)
77
+
78
+ Create a consent gate for an agent message stream.
79
+
80
+ **Options:**
81
+
82
+ | Option | Type | Default | Description |
83
+ |--------|------|---------|-------------|
84
+ | onConsent | function | *required* | Called when consent is needed. Receives { holdId, runId, gapSize, shield, request }. |
85
+ | onAudit | function | null | Called after each decision with { holdId, runId, decision, shield, gapSize, eventCount }. |
86
+ | timeout | number | 60000 | Consent timeout in ms. Default deny after expiry. 0 to disable. |
87
+ | greenShield | boolean | false | Enable Green Shield. When true, Yellow Shield is disabled. |
88
+
89
+ **Returns:** { inspect, resolve, getState }
90
+
91
+ #### gate.inspect(msg, raw, isBinary)
92
+
93
+ Inspect a message from the agent platform. Call for every Gateway-to-Client message.
94
+
95
+ Returns { action } where action is:
96
+ - "forward" — Send to client normally
97
+ - "hold" — Consent requested. Do NOT send. Includes holdId.
98
+ - "drop" — Suppressed. Do NOT send.
99
+ - "buffer" — Added to existing hold. Do NOT send.
100
+
101
+ #### gate.resolve(holdId, decision)
102
+
103
+ Resolve a consent decision. decision is "allow", "deny", or "timeout".
104
+
105
+ Returns { decision, events[] } — on allow, events contains the held
106
+ messages to forward. On deny/timeout, events is empty.
107
+
108
+ ### createAuditLogger(logPath)
109
+
110
+ Create a tamper-evident audit logger.
111
+
112
+ **Returns:** { writeEntry, verifyChain }
113
+
114
+ #### logger.writeEntry(opts)
115
+
116
+ Append a consent decision. opts: { holdId, runId, decision, shield, gapSize, eventCount }.
117
+
118
+ #### logger.verifyChain()
119
+
120
+ Verify hash chain integrity. Returns { valid, entries, brokenAt }.
121
+
122
+ ## Design Principles
123
+
124
+ - **Non-invasive.** The ATL wraps agent platforms. It does not modify them.
125
+ - **Fail-closed.** No response = no permission. Connection drop = denied.
126
+ - **Platform-agnostic.** Works with any agent that streams sequential events.
127
+ - **Honest.** Yellow Shield is consent before delivery, not before execution. We say so.
128
+ - **Auditable.** Every decision is logged with a SHA-256 hash chain.
129
+
130
+ ## License
131
+
132
+ Apache-2.0 — Openly Personal Networks, Inc. (https://opn.li)
133
+
134
+ ## The Agent Economy
135
+
136
+ *My data + Your AI + My control = Living Intelligence*
137
+
138
+ CROCbox is the reference implementation of the ATL. The DevKit is how
139
+ every developer brings trust to their own agents. Together, we are
140
+ building the Agent Economy — where humans control what AI does, not
141
+ the other way around.
142
+
143
+ Learn more at https://opn.li
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@opnli/atl-devkit",
3
+ "version": "0.1.0",
4
+ "description": "Add human consent to any AI agent in 3 lines. The Agent Trust Layer DevKit.",
5
+ "main": "src/index.js",
6
+ "keywords": [
7
+ "ai-agent",
8
+ "trust",
9
+ "consent",
10
+ "agent-trust-layer",
11
+ "human-in-the-loop",
12
+ "opnli",
13
+ "card"
14
+ ],
15
+ "author": "Opn.li — Openly Personal Networks, Inc.",
16
+ "license": "Apache-2.0",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/opnli/agent-trust-layer"
20
+ },
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ }
24
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @opnli/atl-devkit — Audit Logger
3
+ *
4
+ * Append-only JSONL audit log with SHA-256 hash chain.
5
+ * Every consent decision is recorded with timestamp, action, target,
6
+ * result, reason, and a cryptographic link to the previous entry.
7
+ *
8
+ * Tamper-evident: modifying any entry breaks the hash chain.
9
+ * Honest disclosure: tamper-evident, not tamper-proof (INV-16).
10
+ *
11
+ * Extracted from CROCbox ws-proxy.js — proven in production with
12
+ * 100+ entries verified in live testing.
13
+ *
14
+ * @see OPN_ENG_CROC-E2E-Invariants_13MAR26_v2, INV-8, INV-16
15
+ */
16
+ 'use strict';
17
+
18
+ const crypto = require('crypto');
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ /**
23
+ * Create an audit logger that writes to the specified path.
24
+ *
25
+ * @param {string} logPath — Absolute path to the JSONL audit log file.
26
+ * @returns {object} — { writeEntry, verifyChain }
27
+ */
28
+ function createAuditLogger(logPath) {
29
+ // Ensure the directory exists
30
+ const dir = path.dirname(logPath);
31
+ if (!fs.existsSync(dir)) {
32
+ fs.mkdirSync(dir, { recursive: true });
33
+ }
34
+
35
+ /**
36
+ * Append a consent decision to the audit log.
37
+ *
38
+ * @param {object} opts
39
+ * @param {string} opts.holdId — Unique identifier for this consent hold
40
+ * @param {string} opts.runId — Agent run identifier
41
+ * @param {string} opts.decision — 'allow' | 'deny' | 'timeout'
42
+ * @param {string} opts.shield — 'yellow' | 'green'
43
+ * @param {number} opts.gapSize — Seq-gap size (Yellow) or 0 (Green)
44
+ * @param {number} opts.eventCount — Number of held events
45
+ * @param {object} [opts.extra] — Additional fields to include
46
+ * @returns {object} — The written entry (with hash)
47
+ */
48
+ function writeEntry(opts) {
49
+ const { holdId, runId, decision, shield, gapSize, eventCount, extra } = opts;
50
+
51
+ // Read last hash from file
52
+ let prevHash = 'genesis';
53
+ try {
54
+ const content = fs.readFileSync(logPath, 'utf8').trim();
55
+ if (content.length > 0) {
56
+ const lines = content.split('\n');
57
+ const lastEntry = JSON.parse(lines[lines.length - 1]);
58
+ prevHash = lastEntry.hash || 'genesis';
59
+ }
60
+ } catch (e) { /* file may not exist yet — genesis */ }
61
+
62
+ const reasonMap = {
63
+ 'allow': 'user-consent',
64
+ 'deny': 'user-deny',
65
+ 'timeout': 'user-timeout'
66
+ };
67
+ const resultMap = {
68
+ 'allow': 'allowed',
69
+ 'deny': 'blocked',
70
+ 'timeout': 'blocked'
71
+ };
72
+
73
+ const entry = {
74
+ timestamp: new Date().toISOString(),
75
+ action: shield + '-shield',
76
+ target: 'agent-event-stream',
77
+ result: resultMap[decision] || 'blocked',
78
+ reason: reasonMap[decision] || 'unknown',
79
+ detail: 'gap=' + gapSize + ' events-held=' + eventCount + ' holdId=' + holdId,
80
+ decision_id: holdId,
81
+ shield: shield,
82
+ runId: runId,
83
+ prev_hash: prevHash,
84
+ ...(extra || {})
85
+ };
86
+
87
+ const entryStr = JSON.stringify(entry);
88
+ entry.hash = crypto.createHash('sha256').update(entryStr + prevHash).digest('hex');
89
+
90
+ fs.appendFileSync(logPath, JSON.stringify(entry) + '\n');
91
+ return entry;
92
+ }
93
+
94
+ /**
95
+ * Verify the hash chain integrity of the audit log.
96
+ *
97
+ * @returns {object} — { valid: boolean, entries: number, brokenAt: number|null }
98
+ */
99
+ function verifyChain() {
100
+ let content;
101
+ try {
102
+ content = fs.readFileSync(logPath, 'utf8').trim();
103
+ } catch (e) {
104
+ return { valid: true, entries: 0, brokenAt: null };
105
+ }
106
+
107
+ if (content.length === 0) {
108
+ return { valid: true, entries: 0, brokenAt: null };
109
+ }
110
+
111
+ const lines = content.split('\n');
112
+ let expectedPrevHash = 'genesis';
113
+
114
+ for (let i = 0; i < lines.length; i++) {
115
+ try {
116
+ const entry = JSON.parse(lines[i]);
117
+
118
+ // Check prev_hash links to previous entry
119
+ if (entry.prev_hash !== expectedPrevHash) {
120
+ return { valid: false, entries: lines.length, brokenAt: i };
121
+ }
122
+
123
+ // Recompute hash: strip hash field, serialize, hash with prev_hash
124
+ const storedHash = entry.hash;
125
+ const stripped = { ...entry };
126
+ delete stripped.hash;
127
+ const recomputed = crypto.createHash('sha256')
128
+ .update(JSON.stringify(stripped) + expectedPrevHash)
129
+ .digest('hex');
130
+
131
+ if (storedHash !== recomputed) {
132
+ return { valid: false, entries: lines.length, brokenAt: i };
133
+ }
134
+
135
+ expectedPrevHash = storedHash;
136
+ } catch (e) {
137
+ return { valid: false, entries: lines.length, brokenAt: i };
138
+ }
139
+ }
140
+
141
+ return { valid: true, entries: lines.length, brokenAt: null };
142
+ }
143
+
144
+ return { writeEntry, verifyChain };
145
+ }
146
+
147
+ module.exports = { createAuditLogger };
@@ -0,0 +1,280 @@
1
+ /**
2
+ * @opnli/atl-devkit — Consent Gate
3
+ *
4
+ * The core of the Agent Trust Layer. Inspects a stream of messages
5
+ * between an AI agent and its execution environment. When the agent
6
+ * acts on the world, the gate HOLDS the result and asks the human.
7
+ *
8
+ * Two shield levels:
9
+ *
10
+ * GREEN SHIELD (Consent Before Execution):
11
+ * The gate intercepts a pre-execution approval request. The action
12
+ * has NOT happened yet. The human decides whether it happens.
13
+ * Requires the agent platform to broadcast approval events.
14
+ *
15
+ * YELLOW SHIELD (Consent Before Delivery):
16
+ * The gate detects that an action executed via a sequence gap in
17
+ * the agent event stream. The action HAS happened, but the result
18
+ * has not been delivered. The human decides whether to receive it.
19
+ * Works on ANY platform that streams sequential agent events.
20
+ *
21
+ * The gate is fail-closed: if the human doesn't respond within the
22
+ * timeout, the answer is NO. If the connection drops, the answer is
23
+ * NO. There is no bypass.
24
+ *
25
+ * Three-line integration:
26
+ *
27
+ * const { createConsentGate } = require('@opnli/atl-devkit');
28
+ * const gate = createConsentGate({ onConsent: showMyUI, timeout: 60000 });
29
+ * myProxy.onMessage(gate.inspect);
30
+ *
31
+ * @see OPN_ENG_CROC-E2E-Invariants_13MAR26_v2, INV-5, INV-7
32
+ */
33
+ 'use strict';
34
+
35
+ const crypto = require('crypto');
36
+
37
+ /**
38
+ * Create a consent gate for an agent message stream.
39
+ *
40
+ * @param {object} options
41
+ * @param {function} options.onConsent — Called when consent is needed.
42
+ * Receives: { holdId, runId, gapSize, heldEventCount, detectedAt, shield }
43
+ * The integrator must call gate.resolve(holdId, 'allow'|'deny') when
44
+ * the human decides.
45
+ * @param {function} [options.onAudit] — Called with audit entry after each
46
+ * decision. Receives: { holdId, runId, decision, shield, gapSize, eventCount }
47
+ * If not provided, decisions are not logged (bring your own logger).
48
+ * @param {number} [options.timeout=60000] — Consent timeout in ms.
49
+ * Default deny after this period. Set to 0 to disable timeout.
50
+ * @param {boolean} [options.greenShield=false] — Enable Green Shield
51
+ * interception of exec.approval.requested events. When true, Yellow
52
+ * Shield seq-gap detection is disabled (Green supersedes Yellow).
53
+ * @returns {object} — { inspect, resolve, getState }
54
+ */
55
+ function createConsentGate(options) {
56
+ const {
57
+ onConsent,
58
+ onAudit,
59
+ timeout = 60000,
60
+ greenShield = false
61
+ } = options;
62
+
63
+ if (typeof onConsent !== 'function') {
64
+ throw new Error('createConsentGate requires an onConsent callback');
65
+ }
66
+
67
+ // ── Per-runId sequence tracking (Yellow Shield) ────────────
68
+ // Key: runId, Value: { lastSeq, state, holdId }
69
+ // state: 'streaming' | 'holding' | 'allowed' | 'denied'
70
+ const runState = new Map();
71
+
72
+ // ── Held events buffer ─────────────────────────────────────
73
+ // Key: holdId, Value: { runId, events[], gapSize, detectedAt,
74
+ // shield, state: 'pending'|'allow'|'deny'|'timeout' }
75
+ const heldEvents = new Map();
76
+
77
+ // ── Pending Green Shield approvals ─────────────────────────
78
+ // Key: approvalId, Value: { id, request, holdId, createdAtMs, expiresAtMs }
79
+ const pendingApprovals = new Map();
80
+
81
+ /**
82
+ * Resolve a consent decision.
83
+ *
84
+ * @param {string} holdId — The hold to resolve
85
+ * @param {string} decision — 'allow' | 'deny' | 'timeout'
86
+ * @returns {object|null} — { decision, events[] } on allow, null otherwise.
87
+ * The caller is responsible for forwarding the returned events.
88
+ */
89
+ function resolve(holdId, decision) {
90
+ const held = heldEvents.get(holdId);
91
+ if (!held) return null;
92
+ if (held.state !== 'pending') return null;
93
+
94
+ held.state = decision;
95
+
96
+ // Audit callback
97
+ if (typeof onAudit === 'function') {
98
+ onAudit({
99
+ holdId: holdId,
100
+ runId: held.runId,
101
+ decision: decision,
102
+ shield: held.shield,
103
+ gapSize: held.gapSize,
104
+ eventCount: held.events.length
105
+ });
106
+ }
107
+
108
+ if (decision === 'allow') {
109
+ const run = runState.get(held.runId);
110
+ if (run) run.state = 'allowed';
111
+ const events = held.events.slice();
112
+ heldEvents.delete(holdId);
113
+ return { decision: 'allow', events: events };
114
+ } else {
115
+ const run = runState.get(held.runId);
116
+ if (run) run.state = 'denied';
117
+ heldEvents.delete(holdId);
118
+ return { decision: decision, events: [] };
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Start the consent timeout timer for a hold.
124
+ * @private
125
+ */
126
+ function startTimeout(holdId) {
127
+ if (timeout <= 0) return;
128
+ setTimeout(function() {
129
+ const held = heldEvents.get(holdId);
130
+ if (held && held.state === 'pending') {
131
+ resolve(holdId, 'timeout');
132
+ }
133
+ }, timeout);
134
+ }
135
+
136
+ /**
137
+ * Inspect a message from the agent platform.
138
+ *
139
+ * Call this for every message in the Gateway→Client direction.
140
+ * Returns an action telling the caller what to do.
141
+ *
142
+ * @param {object} msg — Parsed JSON message from the agent platform
143
+ * @param {Buffer|string} raw — Raw message data for forwarding
144
+ * @param {boolean} isBinary — Whether the message is binary
145
+ * @returns {object} — { action: 'forward'|'hold'|'drop'|'buffer', ... }
146
+ * 'forward': Send to client normally. msg included.
147
+ * 'hold': Consent requested. Do NOT send to client. holdId included.
148
+ * 'drop': Message suppressed (denied run). Do NOT send to client.
149
+ * 'buffer': Added to existing hold. Do NOT send to client.
150
+ */
151
+ function inspect(msg, raw, isBinary) {
152
+ // ── GREEN SHIELD: Intercept exec.approval.requested ──────
153
+ if (msg.type === 'event' && msg.event === 'exec.approval.requested') {
154
+ const approval = msg.payload;
155
+ if (approval && approval.id) {
156
+ const holdId = 'gs-' + crypto.randomUUID().substring(0, 12);
157
+
158
+ pendingApprovals.set(approval.id, {
159
+ id: approval.id,
160
+ request: approval.request || {},
161
+ holdId: holdId,
162
+ createdAtMs: approval.createdAtMs || Date.now(),
163
+ expiresAtMs: approval.expiresAtMs || (Date.now() + 60000)
164
+ });
165
+
166
+ heldEvents.set(holdId, {
167
+ runId: approval.id,
168
+ events: [],
169
+ gapSize: 0,
170
+ detectedAt: Date.now(),
171
+ shield: 'green',
172
+ state: 'pending',
173
+ approvalId: approval.id
174
+ });
175
+
176
+ onConsent({
177
+ holdId: holdId,
178
+ runId: approval.id,
179
+ gapSize: 0,
180
+ heldEventCount: 0,
181
+ detectedAt: Date.now(),
182
+ shield: 'green',
183
+ request: approval.request || {}
184
+ });
185
+
186
+ startTimeout(holdId);
187
+ return { action: 'hold', holdId: holdId };
188
+ }
189
+ }
190
+
191
+ // Intercept resolved events (don't forward to UI)
192
+ if (msg.type === 'event' && msg.event === 'exec.approval.resolved') {
193
+ return { action: 'drop' };
194
+ }
195
+
196
+ // ── YELLOW SHIELD: Seq-gap detection on agent events ─────
197
+ if (!greenShield && msg.type === 'event' && msg.event === 'agent') {
198
+ const payload = msg.payload || {};
199
+ const runId = payload.runId;
200
+ const seq = payload.seq;
201
+ const stream = payload.stream;
202
+
203
+ if (runId && typeof seq === 'number') {
204
+ if (!runState.has(runId)) {
205
+ runState.set(runId, { lastSeq: 0, state: 'streaming', holdId: null });
206
+ }
207
+ const run = runState.get(runId);
208
+
209
+ // If holding, buffer this event
210
+ if (run.state === 'holding' && run.holdId) {
211
+ const held = heldEvents.get(run.holdId);
212
+ if (held && held.state === 'pending') {
213
+ held.events.push({ data: raw, isBinary: isBinary });
214
+ run.lastSeq = seq;
215
+ return { action: 'buffer', holdId: run.holdId };
216
+ }
217
+ }
218
+
219
+ // If denied, drop all further events for this run
220
+ if (run.state === 'denied') {
221
+ run.lastSeq = seq;
222
+ return { action: 'drop' };
223
+ }
224
+
225
+ // Seq-gap detection on assistant stream
226
+ if (stream === 'assistant' && run.lastSeq > 0 && seq > run.lastSeq + 1) {
227
+ const gapSize = seq - run.lastSeq - 1;
228
+ const holdId = 'ysh-' + crypto.randomUUID().substring(0, 12);
229
+
230
+ run.state = 'holding';
231
+ run.holdId = holdId;
232
+ run.lastSeq = seq;
233
+
234
+ heldEvents.set(holdId, {
235
+ runId: runId,
236
+ events: [{ data: raw, isBinary: isBinary }],
237
+ gapSize: gapSize,
238
+ detectedAt: Date.now(),
239
+ shield: 'yellow',
240
+ state: 'pending'
241
+ });
242
+
243
+ onConsent({
244
+ holdId: holdId,
245
+ runId: runId,
246
+ gapSize: gapSize,
247
+ heldEventCount: 1,
248
+ detectedAt: Date.now(),
249
+ shield: 'yellow'
250
+ });
251
+
252
+ startTimeout(holdId);
253
+ return { action: 'hold', holdId: holdId };
254
+ }
255
+
256
+ // Normal event — track and forward
257
+ run.lastSeq = seq;
258
+ }
259
+ }
260
+
261
+ // ── Default: forward ─────────────────────────────────────
262
+ return { action: 'forward' };
263
+ }
264
+
265
+ /**
266
+ * Get current state for diagnostics.
267
+ * @returns {object} — { activeHolds, trackedRuns, pendingApprovals }
268
+ */
269
+ function getState() {
270
+ return {
271
+ activeHolds: heldEvents.size,
272
+ trackedRuns: runState.size,
273
+ pendingApprovals: pendingApprovals.size
274
+ };
275
+ }
276
+
277
+ return { inspect, resolve, getState };
278
+ }
279
+
280
+ module.exports = { createConsentGate };
package/src/index.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @opnli/atl-devkit — The Agent Trust Layer DevKit
3
+ *
4
+ * Add human consent to any AI agent in 3 lines.
5
+ *
6
+ * const { createConsentGate } = require('@opnli/atl-devkit');
7
+ * const gate = createConsentGate({ onConsent: showMyUI, timeout: 60000 });
8
+ * myProxy.onMessage(gate.inspect);
9
+ *
10
+ * The Agent Trust Layer sits between an AI agent and the world it acts
11
+ * upon. When the agent tries to act — read a file, call an API, run a
12
+ * command — the ATL holds the action and asks the human: Allow or Block?
13
+ *
14
+ * The human's decision is final. The audit trail is tamper-evident.
15
+ * There is no bypass. This is trust infrastructure for the Agent Economy.
16
+ *
17
+ * Two shield levels:
18
+ *
19
+ * GREEN SHIELD — Consent Before Execution. The action has NOT happened
20
+ * yet. The human decides whether it happens. Requires agent platform
21
+ * support for pre-execution approval events.
22
+ *
23
+ * YELLOW SHIELD — Consent Before Delivery. The action HAS happened,
24
+ * but the result has not been delivered. The human decides whether to
25
+ * receive it. Works on ANY platform that streams sequential events.
26
+ *
27
+ * Three Realities of Trust:
28
+ * 1. Identity — Know who is acting (CARD credentials)
29
+ * 2. Consent — Choose what is allowed (this DevKit)
30
+ * 3. Accountability — Check what happened (audit log)
31
+ *
32
+ * My data + Your AI + My control = Living Intelligence
33
+ *
34
+ * @see https://github.com/opnli/agent-trust-layer
35
+ * @see https://opn.li
36
+ *
37
+ * Copyright (c) 2026 Openly Personal Networks, Inc.
38
+ * Licensed under Apache-2.0
39
+ */
40
+ 'use strict';
41
+
42
+ const { createConsentGate } = require('./consent-gate');
43
+ const { createAuditLogger } = require('./audit-logger');
44
+
45
+ module.exports = {
46
+ createConsentGate,
47
+ createAuditLogger
48
+ };