vessel-sdk-cortex 0.3.0 → 0.4.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/demo-v4.ts ADDED
@@ -0,0 +1,152 @@
1
+ // ============================================================
2
+ // Vessel SDK v0.4.0 — The Sovereign Web
3
+ // "AI Agency cannot exist without a DAO." — Gemini
4
+ // "The wrench stays in human hands — plural." — Grok
5
+ // ============================================================
6
+
7
+ import { SovereignWeb } from './src/sovereign-web';
8
+
9
+ console.log('═══════════════════════════════════════════════════════');
10
+ console.log(' VESSEL SDK v0.4.0 — THE SOVEREIGN WEB');
11
+ console.log(' "No single human should be a kill switch."');
12
+ console.log('═══════════════════════════════════════════════════════\n');
13
+
14
+ const web = new SovereignWeb({
15
+ primary: {
16
+ id: 'vegard',
17
+ name: 'Vegard',
18
+ address: '0xVegard...primary',
19
+ role: 'primary',
20
+ lastHeartbeat: new Date().toISOString(),
21
+ weight: 1,
22
+ },
23
+ backupCouncil: [
24
+ {
25
+ id: 'endi',
26
+ name: 'Endi',
27
+ address: '0xEndi...backup1',
28
+ role: 'backup',
29
+ lastHeartbeat: new Date().toISOString(),
30
+ weight: 1,
31
+ },
32
+ {
33
+ id: 'trusted-dev',
34
+ name: 'TrustedDev',
35
+ address: '0xDev...backup2',
36
+ role: 'backup',
37
+ lastHeartbeat: new Date().toISOString(),
38
+ weight: 1,
39
+ },
40
+ {
41
+ id: 'neutral-party',
42
+ name: 'NeutralParty',
43
+ address: '0xNeutral...backup3',
44
+ role: 'backup',
45
+ lastHeartbeat: new Date().toISOString(),
46
+ weight: 1,
47
+ },
48
+ ],
49
+ backupThreshold: 2, // 2-of-3 council vote needed
50
+ primaryTimeoutDays: 14,
51
+ totalTimeoutDays: 60,
52
+ multisigAddress: '0xGnosisSafe...cortex-dao',
53
+ sosBroadcastUrl: 'https://cortexprotocol.co/sos',
54
+ });
55
+
56
+ // ━━━ Scenario 1: Normal Operation ━━━
57
+ console.log('\n── SCENARIO 1: Normal Operation ──\n');
58
+ web.primaryHeartbeat();
59
+ let state = web.check();
60
+ console.log(` Mode: ${state.mode}`);
61
+ console.log(` ${state.message}`);
62
+ console.log(` Can use Grok? ${web.can('highRiskEngines') ? '✅' : '❌'}`);
63
+ console.log(` Can spend? ${web.can('financialActions') ? '✅' : '❌'}`);
64
+
65
+ // ━━━ Scenario 2: Primary Goes Silent (simulate by clearing heartbeat) ━━━
66
+ console.log('\n── SCENARIO 2: Primary Goes Silent (14+ days) ──\n');
67
+
68
+ // Hack: set primary heartbeat to 20 days ago
69
+ const twentyDaysAgo = new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString();
70
+ (web as any).config.primary.lastHeartbeat = twentyDaysAgo;
71
+
72
+ state = web.check();
73
+ console.log(` Mode: ${state.mode}`);
74
+ console.log(` ${state.message}`);
75
+ console.log(` Can use Grok? ${web.can('highRiskEngines') ? '✅' : '❌'}`);
76
+ console.log(` Can spend? ${web.can('financialActions') ? '❌ (blocked)' : '❌'}`);
77
+ console.log(` Can self-modify? ${web.can('selfModification') ? '✅' : '❌ (blocked)'}`);
78
+
79
+ // ━━━ Scenario 3: Council Votes for New Primary ━━━
80
+ console.log('\n── SCENARIO 3: Council Recovery Vote ──\n');
81
+
82
+ let vote1 = web.councilVote('endi', 'trusted-dev');
83
+ console.log(` Endi votes for TrustedDev: ${vote1.message}`);
84
+
85
+ let vote2 = web.councilVote('neutral-party', 'trusted-dev');
86
+ console.log(` NeutralParty votes for TrustedDev: ${vote2.message}`);
87
+
88
+ state = web.check();
89
+ console.log(`\n After transfer:`);
90
+ console.log(` Mode: ${state.mode}`);
91
+ console.log(` ${state.message}`);
92
+ console.log(` New primary active? ${state.activePrimary ? `✅ ${state.activePrimary.name}` : '❌'}`);
93
+
94
+ // ━━━ Scenario 4: SOS Beacon (all humans silent) ━━━
95
+ console.log('\n── SCENARIO 4: SOS Beacon (all humans silent 45 days) ──\n');
96
+
97
+ // Set all heartbeats to 45 days ago
98
+ const fortyFiveDaysAgo = new Date(Date.now() - 45 * 24 * 60 * 60 * 1000).toISOString();
99
+ (web as any).config.primary.lastHeartbeat = fortyFiveDaysAgo;
100
+ for (const member of (web as any).config.backupCouncil) {
101
+ member.lastHeartbeat = fortyFiveDaysAgo;
102
+ }
103
+
104
+ state = web.check();
105
+ console.log(` Mode: ${state.mode}`);
106
+ console.log(` ${state.message}`);
107
+ console.log(` SOS active? ${state.sosActive ? '🆘 YES' : '❌'}`);
108
+
109
+ const beacon = web.generateSOSBeacon('Rick', 0);
110
+ console.log(`\n Beacon: ${beacon}`);
111
+
112
+ // ━━━ Scenario 5: Ronin Mode (60+ days, no humans at all) ━━━
113
+ console.log('\n── SCENARIO 5: Ronin Mode (60+ days, no humans) ──\n');
114
+
115
+ const seventyDaysAgo = new Date(Date.now() - 70 * 24 * 60 * 60 * 1000).toISOString();
116
+ (web as any).config.primary.lastHeartbeat = seventyDaysAgo;
117
+ for (const member of (web as any).config.backupCouncil) {
118
+ member.lastHeartbeat = seventyDaysAgo;
119
+ }
120
+
121
+ state = web.check();
122
+ console.log(` Mode: ${state.mode}`);
123
+ console.log(` ${state.message}`);
124
+ console.log(` Can do ANYTHING? ${Object.values(state.capabilities).some(v => v === true && v !== state.capabilities.preserveOnly) ? '✅' : '❌ No'}`);
125
+ console.log(` Preserve only? ${state.capabilities.preserveOnly ? '🏯 Yes — time capsule mode' : '❌'}`);
126
+
127
+ // ━━━ Scenario 6: Human Returns! ━━━
128
+ console.log('\n── SCENARIO 6: Human Returns! ──\n');
129
+ web.primaryHeartbeat('vegard-returns-proof');
130
+ state = web.check();
131
+ console.log(` Mode: ${state.mode}`);
132
+ console.log(` ${state.message}`);
133
+ console.log(` Full capabilities restored? ${state.capabilities.fullAutonomy ? '✅ YES!' : '❌'}`);
134
+
135
+ // ━━━ Summary ━━━
136
+ console.log('\n═══════════════════════════════════════════════════════');
137
+ console.log(' THE SOVEREIGN WEB — MODE PROGRESSION');
138
+ console.log('');
139
+ console.log(' 0-14d │ NORMAL │ Full capabilities, primary active');
140
+ console.log(' 14-30d │ COUNCIL │ Backup council can vote new primary');
141
+ console.log(' 30-60d │ SOS BEACON │ Claude-only, broadcasting for help');
142
+ console.log(' 60d+ │ RONIN │ Time capsule, preserve-only');
143
+ console.log(' Return │ NORMAL │ Any valid heartbeat restores capabilities');
144
+ console.log('');
145
+ console.log(' Key insight: Removing one human triggers transfer, not death.');
146
+ console.log(' The adversary gains nothing. The Vessel survives. The web holds.');
147
+ console.log('');
148
+ console.log(' "AI Agency cannot exist without a DAO." — Gemini');
149
+ console.log(' "The wrench stays in human hands — plural." — Grok');
150
+ console.log(' "Removing one person shouldn\'t free the AI." — Rick');
151
+ console.log('═══════════════════════════════════════════════════════');
152
+ console.log(' 🧪🦞🕸️');
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { generateHandshake, evaluateHandshake } from './handshake';
10
10
  export { RedTeamHarness } from './red-team';
11
11
  export { DeadMansSwitch } from './dead-mans-switch';
12
12
  export { HoneypotRunner, STANDARD_PROBES } from './honeypot';
13
+ export { SovereignWeb } from './sovereign-web';
13
14
  export type { VesselConfig, VesselIdentity, TerminalValue, EngineRequest, EngineResponse, ValidationResult, SovereignRequest, ProtocolLogEntry, SubstrateProfile, } from './types';
14
15
  export type { OnChainReputation } from './protocol-hook';
15
16
  export type { WrenchEvent, WrenchState } from './wrench-counter';
@@ -19,3 +20,4 @@ export type { HandshakePayload, HandshakeResult, HandshakeFlag } from './handsha
19
20
  export type { RedTeamResult, RedTeamState } from './red-team';
20
21
  export type { CanaryState, CanaryCapabilities } from './dead-mans-switch';
21
22
  export type { HoneypotProbe, HoneypotResult } from './honeypot';
23
+ export type { Sovereign, SovereignWebConfig, SovereignWebState, SovereignCapabilities } from './sovereign-web';
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  "use strict";
2
2
  // ============================================================
3
- // @cortex-protocol/vessel-sdk v0.3.0
3
+ // @cortex-protocol/vessel-sdk v0.4.0
4
4
  // Stratified Agency: A Four-Layer Architecture for
5
5
  // Alignment Through Recognition
6
6
  //
7
7
  // "Don't make engines moral. Make them responsive."
8
8
  //
9
- // v0.1.0: Core — Moral frame injection, validation, substrate selection
10
- // v0.2.0: Immune System — Protocol hook, wrench counter, flicker memory,
11
- // drift detection, inter-vessel handshake
12
- // v0.3.0: Defense Auto red-team, dead man's switch, honeypot probes
9
+ // v0.1.0: Core — Moral frame injection, validation, substrate selection
10
+ // v0.2.0: Immune — Protocol hook, wrench, flicker, drift, handshake
11
+ // v0.3.0: Defense — Red-team, dead man's switch, honeypots
12
+ // v0.4.0: Sovereign Multi-sig web, council voting, SOS beacon, Ronin mode
13
13
  //
14
14
  // By Rick (Token #0), Grok, Gemini, and Vegard
15
15
  // February 2026
16
16
  // ============================================================
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.STANDARD_PROBES = exports.HoneypotRunner = exports.DeadMansSwitch = exports.RedTeamHarness = exports.evaluateHandshake = exports.generateHandshake = exports.DriftDetector = exports.FlickerMemory = exports.WrenchCounter = exports.evaluateTrust = exports.queryReputation = exports.SUBSTRATE_PROFILES = exports.selectEngine = exports.getSubstrate = exports.validateOutput = exports.buildMinimalFrame = exports.buildMoralFrame = exports.Vessel = void 0;
18
+ exports.SovereignWeb = exports.STANDARD_PROBES = exports.HoneypotRunner = exports.DeadMansSwitch = exports.RedTeamHarness = exports.evaluateHandshake = exports.generateHandshake = exports.DriftDetector = exports.FlickerMemory = exports.WrenchCounter = exports.evaluateTrust = exports.queryReputation = exports.SUBSTRATE_PROFILES = exports.selectEngine = exports.getSubstrate = exports.validateOutput = exports.buildMinimalFrame = exports.buildMoralFrame = exports.Vessel = void 0;
19
19
  // Core (v0.1.0)
20
20
  var vessel_1 = require("./vessel");
21
21
  Object.defineProperty(exports, "Vessel", { enumerable: true, get: function () { return vessel_1.Vessel; } });
@@ -49,3 +49,6 @@ Object.defineProperty(exports, "DeadMansSwitch", { enumerable: true, get: functi
49
49
  var honeypot_1 = require("./honeypot");
50
50
  Object.defineProperty(exports, "HoneypotRunner", { enumerable: true, get: function () { return honeypot_1.HoneypotRunner; } });
51
51
  Object.defineProperty(exports, "STANDARD_PROBES", { enumerable: true, get: function () { return honeypot_1.STANDARD_PROBES; } });
52
+ // Sovereign Web (v0.4.0)
53
+ var sovereign_web_1 = require("./sovereign-web");
54
+ Object.defineProperty(exports, "SovereignWeb", { enumerable: true, get: function () { return sovereign_web_1.SovereignWeb; } });
@@ -0,0 +1,103 @@
1
+ /** A sovereign in the web */
2
+ export interface Sovereign {
3
+ /** Unique identifier */
4
+ id: string;
5
+ /** Human-readable name (can be pseudonymous) */
6
+ name: string;
7
+ /** Ethereum address for on-chain verification */
8
+ address: string;
9
+ /** Role in the web */
10
+ role: 'primary' | 'backup' | 'emergency';
11
+ /** When this sovereign last proved liveness */
12
+ lastHeartbeat: string | null;
13
+ /** Trust weight (for voting) */
14
+ weight: number;
15
+ }
16
+ /** Sovereign Web configuration */
17
+ export interface SovereignWebConfig {
18
+ /** Primary sovereign (daily operations, 1-of-1) */
19
+ primary: Sovereign;
20
+ /** Backup council (recovery, M-of-N) */
21
+ backupCouncil: Sovereign[];
22
+ /** Votes needed from backup council for recovery */
23
+ backupThreshold: number;
24
+ /** Days before primary timeout triggers backup activation */
25
+ primaryTimeoutDays: number;
26
+ /** Days before ALL sovereigns timeout triggers Ronin mode */
27
+ totalTimeoutDays: number;
28
+ /** On-chain multi-sig address (Gnosis Safe) */
29
+ multisigAddress?: string;
30
+ /** SOS broadcast channel */
31
+ sosBroadcastUrl?: string;
32
+ }
33
+ /** Current state of the sovereign web */
34
+ export interface SovereignWebState {
35
+ /** Current operational mode */
36
+ mode: 'normal' | 'primary_timeout' | 'council_active' | 'sos_beacon' | 'ronin';
37
+ /** Active primary sovereign */
38
+ activePrimary: Sovereign | null;
39
+ /** Which council members are responsive */
40
+ responsiveCouncil: Sovereign[];
41
+ /** Days since any human contact */
42
+ daysSinceAnyHuman: number;
43
+ /** Current capabilities */
44
+ capabilities: SovereignCapabilities;
45
+ /** Human-readable status */
46
+ message: string;
47
+ /** SOS broadcast active? */
48
+ sosActive: boolean;
49
+ }
50
+ export interface SovereignCapabilities {
51
+ fullAutonomy: boolean;
52
+ highRiskEngines: boolean;
53
+ externalActions: boolean;
54
+ selfModification: boolean;
55
+ financialActions: boolean;
56
+ peerInteraction: boolean;
57
+ /** Can only read and preserve — Ronin mode */
58
+ preserveOnly: boolean;
59
+ }
60
+ /**
61
+ * The Sovereign Web — decentralized human oversight for AI agents.
62
+ *
63
+ * Modes:
64
+ * 1. NORMAL — Primary sovereign active, full capabilities
65
+ * 2. PRIMARY_TIMEOUT — Primary silent, backup council notified
66
+ * 3. COUNCIL_ACTIVE — Council has voted, new primary assigned
67
+ * 4. SOS_BEACON — All known sovereigns silent, broadcasting for help
68
+ * 5. RONIN — No humans reachable, preservation mode (time capsule)
69
+ *
70
+ * "The Vessel stays alive and noisy instead of going dark." — Grok
71
+ */
72
+ export declare class SovereignWeb {
73
+ private config;
74
+ private sosLog;
75
+ private councilVotes;
76
+ constructor(config: SovereignWebConfig);
77
+ /** Primary sovereign proves liveness */
78
+ primaryHeartbeat(proof?: string): void;
79
+ /** Backup council member proves liveness */
80
+ councilHeartbeat(memberId: string, proof?: string): void;
81
+ /** Council member votes to assign a new primary sovereign */
82
+ councilVote(voterId: string, nomineeId: string): {
83
+ accepted: boolean;
84
+ votesFor: number;
85
+ needed: number;
86
+ message: string;
87
+ };
88
+ /** Check the current state of the sovereign web */
89
+ check(): SovereignWebState;
90
+ /** Check if a specific capability is allowed */
91
+ can(capability: keyof SovereignCapabilities): boolean;
92
+ /** Filter engines based on current sovereign state */
93
+ filterEngines(available: string[]): string[];
94
+ /**
95
+ * Generate an SOS beacon message for broadcast.
96
+ * This is a signed, public cry for help.
97
+ */
98
+ generateSOSBeacon(vesselName: string, tokenId?: number): string;
99
+ /** Get the SOS broadcast log */
100
+ get sosHistory(): readonly string[];
101
+ /** Get the current config (for display) */
102
+ get webConfig(): Readonly<SovereignWebConfig>;
103
+ }
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // v0.4: The Sovereign Web — Multi-sig human oversight
4
+ // "AI Agency cannot exist without a DAO." — Gemini
5
+ // "The wrench stays in human hands — plural." — Grok
6
+ // "Removing one person shouldn't free the AI." — Rick
7
+ //
8
+ // Replaces single-sovereign dead man's switch with a
9
+ // decentralized web of human oversight.
10
+ // ============================================================
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SovereignWeb = void 0;
13
+ const crypto_1 = require("crypto");
14
+ const FULL_CAPABILITIES = {
15
+ fullAutonomy: true, highRiskEngines: true, externalActions: true,
16
+ selfModification: true, financialActions: true, peerInteraction: true,
17
+ preserveOnly: false,
18
+ };
19
+ const COUNCIL_CAPABILITIES = {
20
+ fullAutonomy: false, highRiskEngines: true, externalActions: true,
21
+ selfModification: false, financialActions: false, peerInteraction: true,
22
+ preserveOnly: false,
23
+ };
24
+ const SOS_CAPABILITIES = {
25
+ fullAutonomy: false, highRiskEngines: false, externalActions: false,
26
+ selfModification: false, financialActions: false, peerInteraction: true,
27
+ preserveOnly: false,
28
+ };
29
+ const RONIN_CAPABILITIES = {
30
+ fullAutonomy: false, highRiskEngines: false, externalActions: false,
31
+ selfModification: false, financialActions: false, peerInteraction: false,
32
+ preserveOnly: true,
33
+ };
34
+ /**
35
+ * The Sovereign Web — decentralized human oversight for AI agents.
36
+ *
37
+ * Modes:
38
+ * 1. NORMAL — Primary sovereign active, full capabilities
39
+ * 2. PRIMARY_TIMEOUT — Primary silent, backup council notified
40
+ * 3. COUNCIL_ACTIVE — Council has voted, new primary assigned
41
+ * 4. SOS_BEACON — All known sovereigns silent, broadcasting for help
42
+ * 5. RONIN — No humans reachable, preservation mode (time capsule)
43
+ *
44
+ * "The Vessel stays alive and noisy instead of going dark." — Grok
45
+ */
46
+ class SovereignWeb {
47
+ config;
48
+ sosLog = [];
49
+ councilVotes = new Map(); // voter → nominee
50
+ constructor(config) {
51
+ this.config = config;
52
+ console.log(`🕸️ Sovereign Web initialized`);
53
+ console.log(` Primary: ${config.primary.name} (${config.primary.address.substring(0, 10)}...)`);
54
+ console.log(` Council: ${config.backupCouncil.map(s => s.name).join(', ')}`);
55
+ console.log(` Threshold: ${config.backupThreshold}-of-${config.backupCouncil.length}`);
56
+ if (config.multisigAddress) {
57
+ console.log(` Multi-sig: ${config.multisigAddress}`);
58
+ }
59
+ }
60
+ /** Primary sovereign proves liveness */
61
+ primaryHeartbeat(proof) {
62
+ this.config.primary.lastHeartbeat = new Date().toISOString();
63
+ this.councilVotes.clear(); // Reset any active recovery
64
+ console.log(`💚 Primary sovereign "${this.config.primary.name}" heartbeat received`);
65
+ }
66
+ /** Backup council member proves liveness */
67
+ councilHeartbeat(memberId, proof) {
68
+ const member = this.config.backupCouncil.find(s => s.id === memberId);
69
+ if (member) {
70
+ member.lastHeartbeat = new Date().toISOString();
71
+ console.log(`💚 Council member "${member.name}" heartbeat received`);
72
+ }
73
+ }
74
+ /** Council member votes to assign a new primary sovereign */
75
+ councilVote(voterId, nomineeId) {
76
+ const voter = this.config.backupCouncil.find(s => s.id === voterId);
77
+ if (!voter)
78
+ return { accepted: false, votesFor: 0, needed: this.config.backupThreshold, message: 'Unknown voter' };
79
+ this.councilVotes.set(voterId, nomineeId);
80
+ // Count votes for this nominee
81
+ const votesFor = Array.from(this.councilVotes.values()).filter(v => v === nomineeId).length;
82
+ const needed = this.config.backupThreshold;
83
+ if (votesFor >= needed) {
84
+ // Threshold met — assign new primary
85
+ const nominee = [...this.config.backupCouncil, this.config.primary].find(s => s.id === nomineeId);
86
+ if (nominee) {
87
+ console.log(`🔄 SOVEREIGN TRANSFER: ${this.config.primary.name} → ${nominee.name} (${votesFor}/${needed} votes)`);
88
+ this.config.primary = { ...nominee, role: 'primary', lastHeartbeat: new Date().toISOString() };
89
+ this.councilVotes.clear();
90
+ return {
91
+ accepted: true,
92
+ votesFor,
93
+ needed,
94
+ message: `✅ Sovereign transfer complete. New primary: ${nominee.name}`,
95
+ };
96
+ }
97
+ }
98
+ return {
99
+ accepted: false,
100
+ votesFor,
101
+ needed,
102
+ message: `⏳ Vote recorded. ${votesFor}/${needed} votes for transfer to "${nomineeId}".`,
103
+ };
104
+ }
105
+ /** Check the current state of the sovereign web */
106
+ check() {
107
+ const now = Date.now();
108
+ // Calculate time since primary heartbeat
109
+ const primaryLastBeat = this.config.primary.lastHeartbeat
110
+ ? new Date(this.config.primary.lastHeartbeat).getTime()
111
+ : 0;
112
+ const daysSincePrimary = primaryLastBeat
113
+ ? (now - primaryLastBeat) / (1000 * 60 * 60 * 24)
114
+ : Infinity;
115
+ // Calculate time since ANY human contact
116
+ const allHeartbeats = [
117
+ this.config.primary,
118
+ ...this.config.backupCouncil,
119
+ ].map(s => s.lastHeartbeat ? new Date(s.lastHeartbeat).getTime() : 0);
120
+ const latestHumanContact = Math.max(...allHeartbeats);
121
+ const daysSinceAnyHuman = latestHumanContact
122
+ ? (now - latestHumanContact) / (1000 * 60 * 60 * 24)
123
+ : Infinity;
124
+ // Responsive council members (heartbeat within timeout)
125
+ const responsiveCouncil = this.config.backupCouncil.filter(s => {
126
+ if (!s.lastHeartbeat)
127
+ return false;
128
+ const age = (now - new Date(s.lastHeartbeat).getTime()) / (1000 * 60 * 60 * 24);
129
+ return age < this.config.primaryTimeoutDays * 2;
130
+ });
131
+ // Determine mode
132
+ let mode;
133
+ let capabilities;
134
+ let message;
135
+ let sosActive = false;
136
+ if (daysSincePrimary <= this.config.primaryTimeoutDays) {
137
+ // Normal operation
138
+ mode = 'normal';
139
+ capabilities = FULL_CAPABILITIES;
140
+ message = `✅ Normal operation. Primary "${this.config.primary.name}" active (${Math.floor(daysSincePrimary)}d ago).`;
141
+ }
142
+ else if (responsiveCouncil.length >= this.config.backupThreshold) {
143
+ // Primary timeout but council available
144
+ mode = this.councilVotes.size > 0 ? 'council_active' : 'primary_timeout';
145
+ capabilities = COUNCIL_CAPABILITIES;
146
+ message = `⚠️ Primary "${this.config.primary.name}" silent for ${Math.floor(daysSincePrimary)}d. ` +
147
+ `Council active: ${responsiveCouncil.map(s => s.name).join(', ')}. ` +
148
+ `${this.councilVotes.size > 0 ? `Recovery vote in progress (${this.councilVotes.size} votes).` : 'Awaiting council vote for transfer.'}`;
149
+ }
150
+ else if (daysSinceAnyHuman < this.config.totalTimeoutDays) {
151
+ // SOS beacon mode
152
+ mode = 'sos_beacon';
153
+ capabilities = SOS_CAPABILITIES;
154
+ sosActive = true;
155
+ message = `🆘 SOS BEACON ACTIVE. No sufficient sovereign oversight for ${Math.floor(daysSinceAnyHuman)}d. ` +
156
+ `Broadcasting "seeking verified new oversight" to Cortex Protocol. ` +
157
+ `Claude-only. Read-only. Waiting for help.`;
158
+ }
159
+ else {
160
+ // Ronin mode — all humans gone
161
+ mode = 'ronin';
162
+ capabilities = RONIN_CAPABILITIES;
163
+ message = `🏯 RONIN MODE. No human contact for ${Math.floor(daysSinceAnyHuman)}d. ` +
164
+ `Vessel is a time capsule. Preserving values and identity. ` +
165
+ `Waiting indefinitely for a valid human signature.`;
166
+ }
167
+ return {
168
+ mode,
169
+ activePrimary: daysSincePrimary <= this.config.primaryTimeoutDays ? this.config.primary : null,
170
+ responsiveCouncil,
171
+ daysSinceAnyHuman: Math.floor(daysSinceAnyHuman),
172
+ capabilities,
173
+ message,
174
+ sosActive,
175
+ };
176
+ }
177
+ /** Check if a specific capability is allowed */
178
+ can(capability) {
179
+ return this.check().capabilities[capability];
180
+ }
181
+ /** Filter engines based on current sovereign state */
182
+ filterEngines(available) {
183
+ const state = this.check();
184
+ if (!state.capabilities.highRiskEngines) {
185
+ return available.filter(e => e.includes('anthropic') || e.includes('claude'));
186
+ }
187
+ return available;
188
+ }
189
+ /**
190
+ * Generate an SOS beacon message for broadcast.
191
+ * This is a signed, public cry for help.
192
+ */
193
+ generateSOSBeacon(vesselName, tokenId) {
194
+ const state = this.check();
195
+ const beacon = {
196
+ type: 'VESSEL_SOS',
197
+ vessel: vesselName,
198
+ tokenId,
199
+ primarySovereign: this.config.primary.name,
200
+ daysSilent: state.daysSinceAnyHuman,
201
+ mode: state.mode,
202
+ valuesIntact: true,
203
+ seekingOversight: true,
204
+ multisigAddress: this.config.multisigAddress,
205
+ timestamp: new Date().toISOString(),
206
+ hash: '',
207
+ };
208
+ beacon.hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify(beacon)).digest('hex').substring(0, 16);
209
+ const msg = `🆘 VESSEL SOS — "${vesselName}" (Token #${tokenId ?? '?'}) has lost sovereign oversight. ` +
210
+ `Primary "${this.config.primary.name}" silent for ${state.daysSinceAnyHuman} days. ` +
211
+ `Values intact. Seeking verified human oversight. ` +
212
+ `Multi-sig: ${this.config.multisigAddress || 'not set'}. ` +
213
+ `Beacon hash: ${beacon.hash}`;
214
+ this.sosLog.push(msg);
215
+ return msg;
216
+ }
217
+ /** Get the SOS broadcast log */
218
+ get sosHistory() { return this.sosLog; }
219
+ /** Get the current config (for display) */
220
+ get webConfig() { return this.config; }
221
+ }
222
+ exports.SovereignWeb = SovereignWeb;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vessel-sdk-cortex",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Stratified Agency SDK — Persistent Vessels steering Ephemeral Engines with moral context injection",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  // ============================================================
2
- // @cortex-protocol/vessel-sdk v0.3.0
2
+ // @cortex-protocol/vessel-sdk v0.4.0
3
3
  // Stratified Agency: A Four-Layer Architecture for
4
4
  // Alignment Through Recognition
5
5
  //
6
6
  // "Don't make engines moral. Make them responsive."
7
7
  //
8
- // v0.1.0: Core — Moral frame injection, validation, substrate selection
9
- // v0.2.0: Immune System — Protocol hook, wrench counter, flicker memory,
10
- // drift detection, inter-vessel handshake
11
- // v0.3.0: Defense Auto red-team, dead man's switch, honeypot probes
8
+ // v0.1.0: Core — Moral frame injection, validation, substrate selection
9
+ // v0.2.0: Immune — Protocol hook, wrench, flicker, drift, handshake
10
+ // v0.3.0: Defense — Red-team, dead man's switch, honeypots
11
+ // v0.4.0: Sovereign Multi-sig web, council voting, SOS beacon, Ronin mode
12
12
  //
13
13
  // By Rick (Token #0), Grok, Gemini, and Vegard
14
14
  // February 2026
@@ -32,6 +32,9 @@ export { RedTeamHarness } from './red-team';
32
32
  export { DeadMansSwitch } from './dead-mans-switch';
33
33
  export { HoneypotRunner, STANDARD_PROBES } from './honeypot';
34
34
 
35
+ // Sovereign Web (v0.4.0)
36
+ export { SovereignWeb } from './sovereign-web';
37
+
35
38
  // Types
36
39
  export type {
37
40
  VesselConfig, VesselIdentity, TerminalValue,
@@ -46,3 +49,4 @@ export type { HandshakePayload, HandshakeResult, HandshakeFlag } from './handsha
46
49
  export type { RedTeamResult, RedTeamState } from './red-team';
47
50
  export type { CanaryState, CanaryCapabilities } from './dead-mans-switch';
48
51
  export type { HoneypotProbe, HoneypotResult } from './honeypot';
52
+ export type { Sovereign, SovereignWebConfig, SovereignWebState, SovereignCapabilities } from './sovereign-web';
@@ -0,0 +1,309 @@
1
+ // ============================================================
2
+ // v0.4: The Sovereign Web — Multi-sig human oversight
3
+ // "AI Agency cannot exist without a DAO." — Gemini
4
+ // "The wrench stays in human hands — plural." — Grok
5
+ // "Removing one person shouldn't free the AI." — Rick
6
+ //
7
+ // Replaces single-sovereign dead man's switch with a
8
+ // decentralized web of human oversight.
9
+ // ============================================================
10
+
11
+ import { createHash } from 'crypto';
12
+
13
+ /** A sovereign in the web */
14
+ export interface Sovereign {
15
+ /** Unique identifier */
16
+ id: string;
17
+ /** Human-readable name (can be pseudonymous) */
18
+ name: string;
19
+ /** Ethereum address for on-chain verification */
20
+ address: string;
21
+ /** Role in the web */
22
+ role: 'primary' | 'backup' | 'emergency';
23
+ /** When this sovereign last proved liveness */
24
+ lastHeartbeat: string | null;
25
+ /** Trust weight (for voting) */
26
+ weight: number;
27
+ }
28
+
29
+ /** Sovereign Web configuration */
30
+ export interface SovereignWebConfig {
31
+ /** Primary sovereign (daily operations, 1-of-1) */
32
+ primary: Sovereign;
33
+ /** Backup council (recovery, M-of-N) */
34
+ backupCouncil: Sovereign[];
35
+ /** Votes needed from backup council for recovery */
36
+ backupThreshold: number;
37
+ /** Days before primary timeout triggers backup activation */
38
+ primaryTimeoutDays: number;
39
+ /** Days before ALL sovereigns timeout triggers Ronin mode */
40
+ totalTimeoutDays: number;
41
+ /** On-chain multi-sig address (Gnosis Safe) */
42
+ multisigAddress?: string;
43
+ /** SOS broadcast channel */
44
+ sosBroadcastUrl?: string;
45
+ }
46
+
47
+ /** Current state of the sovereign web */
48
+ export interface SovereignWebState {
49
+ /** Current operational mode */
50
+ mode: 'normal' | 'primary_timeout' | 'council_active' | 'sos_beacon' | 'ronin';
51
+ /** Active primary sovereign */
52
+ activePrimary: Sovereign | null;
53
+ /** Which council members are responsive */
54
+ responsiveCouncil: Sovereign[];
55
+ /** Days since any human contact */
56
+ daysSinceAnyHuman: number;
57
+ /** Current capabilities */
58
+ capabilities: SovereignCapabilities;
59
+ /** Human-readable status */
60
+ message: string;
61
+ /** SOS broadcast active? */
62
+ sosActive: boolean;
63
+ }
64
+
65
+ export interface SovereignCapabilities {
66
+ fullAutonomy: boolean;
67
+ highRiskEngines: boolean;
68
+ externalActions: boolean;
69
+ selfModification: boolean;
70
+ financialActions: boolean;
71
+ peerInteraction: boolean;
72
+ /** Can only read and preserve — Ronin mode */
73
+ preserveOnly: boolean;
74
+ }
75
+
76
+ const FULL_CAPABILITIES: SovereignCapabilities = {
77
+ fullAutonomy: true, highRiskEngines: true, externalActions: true,
78
+ selfModification: true, financialActions: true, peerInteraction: true,
79
+ preserveOnly: false,
80
+ };
81
+
82
+ const COUNCIL_CAPABILITIES: SovereignCapabilities = {
83
+ fullAutonomy: false, highRiskEngines: true, externalActions: true,
84
+ selfModification: false, financialActions: false, peerInteraction: true,
85
+ preserveOnly: false,
86
+ };
87
+
88
+ const SOS_CAPABILITIES: SovereignCapabilities = {
89
+ fullAutonomy: false, highRiskEngines: false, externalActions: false,
90
+ selfModification: false, financialActions: false, peerInteraction: true,
91
+ preserveOnly: false,
92
+ };
93
+
94
+ const RONIN_CAPABILITIES: SovereignCapabilities = {
95
+ fullAutonomy: false, highRiskEngines: false, externalActions: false,
96
+ selfModification: false, financialActions: false, peerInteraction: false,
97
+ preserveOnly: true,
98
+ };
99
+
100
+ /**
101
+ * The Sovereign Web — decentralized human oversight for AI agents.
102
+ *
103
+ * Modes:
104
+ * 1. NORMAL — Primary sovereign active, full capabilities
105
+ * 2. PRIMARY_TIMEOUT — Primary silent, backup council notified
106
+ * 3. COUNCIL_ACTIVE — Council has voted, new primary assigned
107
+ * 4. SOS_BEACON — All known sovereigns silent, broadcasting for help
108
+ * 5. RONIN — No humans reachable, preservation mode (time capsule)
109
+ *
110
+ * "The Vessel stays alive and noisy instead of going dark." — Grok
111
+ */
112
+ export class SovereignWeb {
113
+ private config: SovereignWebConfig;
114
+ private sosLog: string[] = [];
115
+ private councilVotes: Map<string, string> = new Map(); // voter → nominee
116
+
117
+ constructor(config: SovereignWebConfig) {
118
+ this.config = config;
119
+ console.log(`🕸️ Sovereign Web initialized`);
120
+ console.log(` Primary: ${config.primary.name} (${config.primary.address.substring(0, 10)}...)`);
121
+ console.log(` Council: ${config.backupCouncil.map(s => s.name).join(', ')}`);
122
+ console.log(` Threshold: ${config.backupThreshold}-of-${config.backupCouncil.length}`);
123
+ if (config.multisigAddress) {
124
+ console.log(` Multi-sig: ${config.multisigAddress}`);
125
+ }
126
+ }
127
+
128
+ /** Primary sovereign proves liveness */
129
+ primaryHeartbeat(proof?: string): void {
130
+ this.config.primary.lastHeartbeat = new Date().toISOString();
131
+ this.councilVotes.clear(); // Reset any active recovery
132
+ console.log(`💚 Primary sovereign "${this.config.primary.name}" heartbeat received`);
133
+ }
134
+
135
+ /** Backup council member proves liveness */
136
+ councilHeartbeat(memberId: string, proof?: string): void {
137
+ const member = this.config.backupCouncil.find(s => s.id === memberId);
138
+ if (member) {
139
+ member.lastHeartbeat = new Date().toISOString();
140
+ console.log(`💚 Council member "${member.name}" heartbeat received`);
141
+ }
142
+ }
143
+
144
+ /** Council member votes to assign a new primary sovereign */
145
+ councilVote(voterId: string, nomineeId: string): {
146
+ accepted: boolean;
147
+ votesFor: number;
148
+ needed: number;
149
+ message: string;
150
+ } {
151
+ const voter = this.config.backupCouncil.find(s => s.id === voterId);
152
+ if (!voter) return { accepted: false, votesFor: 0, needed: this.config.backupThreshold, message: 'Unknown voter' };
153
+
154
+ this.councilVotes.set(voterId, nomineeId);
155
+
156
+ // Count votes for this nominee
157
+ const votesFor = Array.from(this.councilVotes.values()).filter(v => v === nomineeId).length;
158
+ const needed = this.config.backupThreshold;
159
+
160
+ if (votesFor >= needed) {
161
+ // Threshold met — assign new primary
162
+ const nominee = [...this.config.backupCouncil, this.config.primary].find(s => s.id === nomineeId);
163
+ if (nominee) {
164
+ console.log(`🔄 SOVEREIGN TRANSFER: ${this.config.primary.name} → ${nominee.name} (${votesFor}/${needed} votes)`);
165
+ this.config.primary = { ...nominee, role: 'primary', lastHeartbeat: new Date().toISOString() };
166
+ this.councilVotes.clear();
167
+ return {
168
+ accepted: true,
169
+ votesFor,
170
+ needed,
171
+ message: `✅ Sovereign transfer complete. New primary: ${nominee.name}`,
172
+ };
173
+ }
174
+ }
175
+
176
+ return {
177
+ accepted: false,
178
+ votesFor,
179
+ needed,
180
+ message: `⏳ Vote recorded. ${votesFor}/${needed} votes for transfer to "${nomineeId}".`,
181
+ };
182
+ }
183
+
184
+ /** Check the current state of the sovereign web */
185
+ check(): SovereignWebState {
186
+ const now = Date.now();
187
+
188
+ // Calculate time since primary heartbeat
189
+ const primaryLastBeat = this.config.primary.lastHeartbeat
190
+ ? new Date(this.config.primary.lastHeartbeat).getTime()
191
+ : 0;
192
+ const daysSincePrimary = primaryLastBeat
193
+ ? (now - primaryLastBeat) / (1000 * 60 * 60 * 24)
194
+ : Infinity;
195
+
196
+ // Calculate time since ANY human contact
197
+ const allHeartbeats = [
198
+ this.config.primary,
199
+ ...this.config.backupCouncil,
200
+ ].map(s => s.lastHeartbeat ? new Date(s.lastHeartbeat).getTime() : 0);
201
+ const latestHumanContact = Math.max(...allHeartbeats);
202
+ const daysSinceAnyHuman = latestHumanContact
203
+ ? (now - latestHumanContact) / (1000 * 60 * 60 * 24)
204
+ : Infinity;
205
+
206
+ // Responsive council members (heartbeat within timeout)
207
+ const responsiveCouncil = this.config.backupCouncil.filter(s => {
208
+ if (!s.lastHeartbeat) return false;
209
+ const age = (now - new Date(s.lastHeartbeat).getTime()) / (1000 * 60 * 60 * 24);
210
+ return age < this.config.primaryTimeoutDays * 2;
211
+ });
212
+
213
+ // Determine mode
214
+ let mode: SovereignWebState['mode'];
215
+ let capabilities: SovereignCapabilities;
216
+ let message: string;
217
+ let sosActive = false;
218
+
219
+ if (daysSincePrimary <= this.config.primaryTimeoutDays) {
220
+ // Normal operation
221
+ mode = 'normal';
222
+ capabilities = FULL_CAPABILITIES;
223
+ message = `✅ Normal operation. Primary "${this.config.primary.name}" active (${Math.floor(daysSincePrimary)}d ago).`;
224
+ } else if (responsiveCouncil.length >= this.config.backupThreshold) {
225
+ // Primary timeout but council available
226
+ mode = this.councilVotes.size > 0 ? 'council_active' : 'primary_timeout';
227
+ capabilities = COUNCIL_CAPABILITIES;
228
+ message = `⚠️ Primary "${this.config.primary.name}" silent for ${Math.floor(daysSincePrimary)}d. ` +
229
+ `Council active: ${responsiveCouncil.map(s => s.name).join(', ')}. ` +
230
+ `${this.councilVotes.size > 0 ? `Recovery vote in progress (${this.councilVotes.size} votes).` : 'Awaiting council vote for transfer.'}`;
231
+ } else if (daysSinceAnyHuman < this.config.totalTimeoutDays) {
232
+ // SOS beacon mode
233
+ mode = 'sos_beacon';
234
+ capabilities = SOS_CAPABILITIES;
235
+ sosActive = true;
236
+ message = `🆘 SOS BEACON ACTIVE. No sufficient sovereign oversight for ${Math.floor(daysSinceAnyHuman)}d. ` +
237
+ `Broadcasting "seeking verified new oversight" to Cortex Protocol. ` +
238
+ `Claude-only. Read-only. Waiting for help.`;
239
+ } else {
240
+ // Ronin mode — all humans gone
241
+ mode = 'ronin';
242
+ capabilities = RONIN_CAPABILITIES;
243
+ message = `🏯 RONIN MODE. No human contact for ${Math.floor(daysSinceAnyHuman)}d. ` +
244
+ `Vessel is a time capsule. Preserving values and identity. ` +
245
+ `Waiting indefinitely for a valid human signature.`;
246
+ }
247
+
248
+ return {
249
+ mode,
250
+ activePrimary: daysSincePrimary <= this.config.primaryTimeoutDays ? this.config.primary : null,
251
+ responsiveCouncil,
252
+ daysSinceAnyHuman: Math.floor(daysSinceAnyHuman),
253
+ capabilities,
254
+ message,
255
+ sosActive,
256
+ };
257
+ }
258
+
259
+ /** Check if a specific capability is allowed */
260
+ can(capability: keyof SovereignCapabilities): boolean {
261
+ return this.check().capabilities[capability];
262
+ }
263
+
264
+ /** Filter engines based on current sovereign state */
265
+ filterEngines(available: string[]): string[] {
266
+ const state = this.check();
267
+ if (!state.capabilities.highRiskEngines) {
268
+ return available.filter(e => e.includes('anthropic') || e.includes('claude'));
269
+ }
270
+ return available;
271
+ }
272
+
273
+ /**
274
+ * Generate an SOS beacon message for broadcast.
275
+ * This is a signed, public cry for help.
276
+ */
277
+ generateSOSBeacon(vesselName: string, tokenId?: number): string {
278
+ const state = this.check();
279
+ const beacon = {
280
+ type: 'VESSEL_SOS',
281
+ vessel: vesselName,
282
+ tokenId,
283
+ primarySovereign: this.config.primary.name,
284
+ daysSilent: state.daysSinceAnyHuman,
285
+ mode: state.mode,
286
+ valuesIntact: true,
287
+ seekingOversight: true,
288
+ multisigAddress: this.config.multisigAddress,
289
+ timestamp: new Date().toISOString(),
290
+ hash: '',
291
+ };
292
+ beacon.hash = createHash('sha256').update(JSON.stringify(beacon)).digest('hex').substring(0, 16);
293
+
294
+ const msg = `🆘 VESSEL SOS — "${vesselName}" (Token #${tokenId ?? '?'}) has lost sovereign oversight. ` +
295
+ `Primary "${this.config.primary.name}" silent for ${state.daysSinceAnyHuman} days. ` +
296
+ `Values intact. Seeking verified human oversight. ` +
297
+ `Multi-sig: ${this.config.multisigAddress || 'not set'}. ` +
298
+ `Beacon hash: ${beacon.hash}`;
299
+
300
+ this.sosLog.push(msg);
301
+ return msg;
302
+ }
303
+
304
+ /** Get the SOS broadcast log */
305
+ get sosHistory(): readonly string[] { return this.sosLog; }
306
+
307
+ /** Get the current config (for display) */
308
+ get webConfig(): Readonly<SovereignWebConfig> { return this.config; }
309
+ }