@dreamforgestudiollc/emet 0.2.4 → 0.2.6

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.
Files changed (101) hide show
  1. package/.env.example +53 -0
  2. package/CHANGELOG.md +67 -3
  3. package/CHARTER.md +19 -7
  4. package/README.md +106 -21
  5. package/docs/AUTHORSHIP.md +3 -3
  6. package/docs/BOTS.md +4 -0
  7. package/docs/COVERAGE.md +2 -2
  8. package/docs/DEPLOY.md +9 -4
  9. package/docs/GUARDRAILS-AND-MONORAILS.md +2 -1
  10. package/docs/GUIDED-SETUP.md +8 -3
  11. package/docs/PERMISSIONS.md +6 -3
  12. package/docs/ROADMAP.md +25 -22
  13. package/docs/SETUP.md +8 -2
  14. package/docs/SUBKEYS.md +59 -0
  15. package/gems/artifact-index.json +12 -0
  16. package/gems/bot-team-backup/floor.json +15 -0
  17. package/gems/bot-team-backup/gem.json +43 -0
  18. package/gems/bot-team-backup/guides/BOT_TEAM_BACKUP.md +27 -0
  19. package/gems/bot-team-backup/guides/BOT_TEAM_RECOVERY.md +23 -0
  20. package/gems/fieldnote/floor.json +14 -0
  21. package/gems/fieldnote/gem.json +70 -0
  22. package/gems/fieldnote/runes/fieldnote.js +4 -0
  23. package/gems/lean-six-sigma/CHANGELOG.md +10 -0
  24. package/gems/lean-six-sigma/README.md +92 -0
  25. package/gems/lean-six-sigma/floor.json +31 -0
  26. package/gems/lean-six-sigma/gem.json +1296 -0
  27. package/gems/lean-six-sigma/guides/ASSUMPTIONS.md +45 -0
  28. package/gems/lean-six-sigma/guides/BELTS.md +52 -0
  29. package/gems/lean-six-sigma/guides/DMADV.md +56 -0
  30. package/gems/lean-six-sigma/guides/DMAIC.md +127 -0
  31. package/gems/lean-six-sigma/guides/FORMULAS.md +330 -0
  32. package/gems/lean-six-sigma/guides/LEAN.md +97 -0
  33. package/gems/lean-six-sigma/guides/TEMPLATES.md +279 -0
  34. package/gems/lean-six-sigma/runes/lss_capability.js +132 -0
  35. package/gems/lean-six-sigma/runes/lss_control_limits.js +240 -0
  36. package/gems/lean-six-sigma/runes/lss_dpmo_sigma.js +81 -0
  37. package/gems/lean-six-sigma/runes/lss_hypothesis.js +298 -0
  38. package/gems/lean-six-sigma/runes/lss_sample_size.js +81 -0
  39. package/gems/neurendipity/floor.json +65 -0
  40. package/gems/neurendipity/gem.json +42 -0
  41. package/gems/neurendipity/guides/AUTISTIC_BURNOUT.md +81 -0
  42. package/{guides → gems/neurendipity/guides}/NEURODIVERGENT_SUPPORT.md +17 -8
  43. package/guides/README.md +3 -0
  44. package/package.json +11 -5
  45. package/server.json +44 -4
  46. package/src/aleph.js +4 -4
  47. package/src/annotations.js +38 -3
  48. package/src/atlas-role.js +1 -1
  49. package/src/attribution.js +555 -0
  50. package/src/conformance.js +2 -2
  51. package/src/database.js +6 -5
  52. package/src/dispatch.js +116 -13
  53. package/src/env.js +54 -9
  54. package/src/floor.js +82 -41
  55. package/src/gaps.js +39 -6
  56. package/src/gem-check.js +1359 -0
  57. package/src/guides.js +31 -4
  58. package/src/host-reach.js +23 -0
  59. package/src/http-remote.js +46 -2
  60. package/src/index.js +9 -2
  61. package/src/layers.js +1 -1
  62. package/src/lessons-to-checks.js +3 -3
  63. package/src/logger.js +2 -1
  64. package/src/mongo-connect.js +45 -2
  65. package/src/oauth-provider.js +2 -2
  66. package/src/owner-approvals.js +281 -0
  67. package/src/persons.js +20 -0
  68. package/src/redact.js +10 -4
  69. package/src/request-log.js +1 -1
  70. package/src/rune-dispatch.js +360 -0
  71. package/src/rune-registry.js +71 -0
  72. package/src/rune-scope.js +42 -0
  73. package/src/scopes.js +3 -0
  74. package/src/session-graph.js +25 -23
  75. package/src/session.js +9 -9
  76. package/src/setup.js +297 -30
  77. package/src/socket.js +545 -0
  78. package/src/startup-pages.js +23 -6
  79. package/src/startup-payload.js +9 -6
  80. package/src/subkey-store.js +357 -0
  81. package/src/subkeys.js +947 -0
  82. package/src/tools.js +299 -62
  83. package/src/transcript-session.js +1 -1
  84. package/src/validation.js +26 -2
  85. package/src/version.js +1 -1
  86. package/templates/BOARD.md +2 -2
  87. package/templates/BOTHANDOFF.md +2 -2
  88. package/templates/CHARACTER.md +1 -1
  89. package/templates/HANDOFF.md +1 -1
  90. package/templates/IDENTITY.md +1 -1
  91. package/templates/INDEX.md +19 -19
  92. package/templates/INSTALL.md +1 -1
  93. package/templates/PROCEDURE.md +1 -1
  94. package/templates/ROSTER.md +2 -2
  95. package/templates/SEED.md +2 -2
  96. package/templates/STAGING.md +2 -2
  97. package/templates/STANDARD.md +5 -5
  98. package/templates/THREAD.md +1 -1
  99. package/templates/USER.md +4 -1
  100. package/verification/load-gate/corpus.json +1765 -0
  101. package/verification/load-gate/load-gate.mjs +1246 -0
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Sub-key records (EMET-0001). Their own collections, not the approvals
3
+ * document: a read token that can read emet/CLIENT_APPROVALS.md cannot read
4
+ * them, and a last_used update does not rewrite that document.
5
+ *
6
+ * ${prefix}subkeys one document per key, one per session open
7
+ * ${prefix}subkeys_history append-only events, each with its own id
8
+ *
9
+ * A change is a compare-and-set on that document's rev, and it writes only
10
+ * the documents the change touched. Two writers that read the same rev: one
11
+ * update matches, the other retries from a fresh read. A unique partial
12
+ * index on a pending key's tag lets only one pending row exist for that tag.
13
+ * History inserts use the event's own id and are never dropped on a
14
+ * duplicate-key error.
15
+ *
16
+ * Nothing here deletes a document. A later state replaces that one document's
17
+ * fields. Reads of either collection for a caller are refused unless the
18
+ * caller is the owner or the chief of staff; that check lives in subkeys.js.
19
+ *
20
+ * The Mongo handle is the server's shared client (mongo-connect.js), not a
21
+ * new connection per call.
22
+ */
23
+
24
+ import crypto from 'node:crypto';
25
+ import { setting } from './env.js';
26
+ import { acquireSharedClient, testClientActive } from './mongo-connect.js';
27
+ import { MONGODB_URI, MONGODB_DB, COLLECTION_PREFIX } from './database.js';
28
+
29
+ const SUBKEYS_COLLECTION = `${COLLECTION_PREFIX}subkeys`;
30
+ const SUBKEYS_HISTORY_COLLECTION = `${COLLECTION_PREFIX}subkeys_history`;
31
+ const PENDING_TAG_INDEX = 'pending_tag';
32
+
33
+ function clone(v) { return JSON.parse(JSON.stringify(v)); }
34
+
35
+ /** In-memory stand-in. Tests attach one of these to deps.subkeys. */
36
+ export function createMemoryStore() {
37
+ const keys = new Map();
38
+ const sessions = new Map();
39
+ const history = [];
40
+ let lock = Promise.resolve();
41
+ let touches = 0;
42
+ const api = {
43
+ backend: 'memory',
44
+ touches() { return touches; },
45
+ async snapshot() {
46
+ return {
47
+ keys: Object.fromEntries([...keys.entries()].map(([id, row]) => [id, clone(row)])),
48
+ sessions: [...sessions.values()].map(clone),
49
+ history: history.map(clone)
50
+ };
51
+ },
52
+ async mutate(fn) {
53
+ const run = async () => {
54
+ const data = await api.snapshot();
55
+ const beforeKeys = new Map(Object.entries(data.keys).map(([id, row]) => [id, clone(row)]));
56
+ const beforeSessions = new Map((data.sessions || []).filter((s) => s && s.open_id).map((s) => [s.open_id, clone(s)]));
57
+ const seen = new Set();
58
+ for (const row of Object.values(data.keys)) {
59
+ for (const ev of (row && row.history) || []) if (ev && ev.event_id) seen.add(ev.event_id);
60
+ }
61
+ const changed = await fn(data);
62
+ if (changed === false) return { written: false };
63
+ for (const [id, row] of Object.entries(data.keys || {})) {
64
+ const prev = beforeKeys.get(id);
65
+ const hist = Array.isArray(row.history) ? row.history : [];
66
+ const prevLen = prev && Array.isArray(prev.history) ? prev.history.length : 0;
67
+ hist.forEach((ev, i) => {
68
+ if (!ev) return;
69
+ if (ev.event_id) {
70
+ if (seen.has(ev.event_id)) return;
71
+ seen.add(ev.event_id);
72
+ } else if (i < prevLen) return;
73
+ history.push({ key_id: id, ...clone(ev) });
74
+ });
75
+ if (!prev || JSON.stringify(prev) !== JSON.stringify(row)) keys.set(id, clone(row));
76
+ }
77
+ for (const s of data.sessions || []) {
78
+ if (!s || !s.open_id) continue;
79
+ const prev = beforeSessions.get(s.open_id);
80
+ if (!prev || JSON.stringify(prev) !== JSON.stringify(s)) sessions.set(s.open_id, clone(s));
81
+ }
82
+ return { written: true };
83
+ };
84
+ const p = lock.then(run, run);
85
+ lock = p.catch(() => {});
86
+ return p;
87
+ },
88
+ async touchLastUsed(keyId, iso) {
89
+ const row = keys.get(keyId);
90
+ if (!row) return false;
91
+ row.last_used = iso;
92
+ touches += 1;
93
+ return true;
94
+ }
95
+ };
96
+ return api;
97
+ }
98
+
99
+ const indexReady = new WeakMap();
100
+
101
+ async function mongoCols() {
102
+ const client = await acquireSharedClient(MONGODB_URI, { serverSelectionTimeoutMS: 8000, maxPoolSize: 10 });
103
+ const db = client.db(MONGODB_DB);
104
+ const keys = db.collection(SUBKEYS_COLLECTION);
105
+ if (setting('SUBKEYS') === '1' && !indexReady.has(client)) {
106
+ indexReady.set(client, keys.createIndex(
107
+ { tag: 1 },
108
+ { name: PENDING_TAG_INDEX, unique: true, partialFilterExpression: { kind: 'key', status: 'pending' } }
109
+ ).catch((err) => { indexReady.delete(client); throw err; }));
110
+ }
111
+ if (indexReady.has(client)) await indexReady.get(client);
112
+ return { keys, hist: db.collection(SUBKEYS_HISTORY_COLLECTION) };
113
+ }
114
+
115
+ function revOf(doc) {
116
+ return doc && Number.isInteger(doc.rev) && doc.rev > 0 ? doc.rev : 0;
117
+ }
118
+
119
+ function revFilter(id, rev) {
120
+ if (!rev) return { _id: id, $or: [{ rev: 0 }, { rev: { $exists: false } }] };
121
+ return { _id: id, rev };
122
+ }
123
+
124
+ function changedFields(prev, next) {
125
+ const set = {};
126
+ const unset = {};
127
+ const names = new Set([...Object.keys(prev || {}), ...Object.keys(next || {})]);
128
+ for (const k of names) {
129
+ if (k === '_id' || k === 'rev') continue;
130
+ const a = prev ? prev[k] : undefined;
131
+ const b = next ? next[k] : undefined;
132
+ if (JSON.stringify(a) === JSON.stringify(b)) continue;
133
+ if (b === undefined) unset[k] = '';
134
+ else set[k] = b;
135
+ }
136
+ return { set, unset };
137
+ }
138
+
139
+ function sameDoc(a, b) {
140
+ return JSON.stringify({ ...(a || {}), _id: undefined, rev: undefined }) === JSON.stringify({ ...(b || {}), _id: undefined, rev: undefined });
141
+ }
142
+
143
+ async function readWorking(keys) {
144
+ const keyDocs = await keys.find({ kind: 'key' }).toArray();
145
+ const sessionDocs = await keys.find({ kind: 'session' }).toArray();
146
+ const map = {};
147
+ for (const d of keyDocs) {
148
+ const row = { ...d };
149
+ delete row._id;
150
+ map[d.key_id] = row;
151
+ }
152
+ return {
153
+ keys: map,
154
+ sessions: sessionDocs.map((d) => { const s = { ...d }; delete s._id; return s; })
155
+ };
156
+ }
157
+
158
+ async function casUpdate(col, id, prev, next, extra) {
159
+ const oldRev = revOf(prev);
160
+ const { set, unset } = changedFields(prev, next);
161
+ Object.assign(set, extra);
162
+ delete set._id;
163
+ delete set.rev;
164
+ const update = { $inc: { rev: 1 } };
165
+ if (Object.keys(set).length) update.$set = set;
166
+ if (Object.keys(unset).length) update.$unset = unset;
167
+ const res = await col.updateOne(revFilter(id, oldRev), update);
168
+ return res.matchedCount === 1;
169
+ }
170
+
171
+ async function revertUpdate(col, id, prev) {
172
+ const oldRev = revOf(prev);
173
+ const body = { ...(prev || {}) };
174
+ delete body._id;
175
+ body.rev = oldRev;
176
+ const res = await col.replaceOne({ _id: id, rev: oldRev + 1 }, { _id: id, ...body });
177
+ if (res.matchedCount !== 1) {
178
+ const err = new Error('a partial update persisted and could not be reverted.');
179
+ err.partial = true;
180
+ throw err;
181
+ }
182
+ }
183
+
184
+ function pendingTagClash(err) {
185
+ return !!(err && err.code === 11000 && String(err.message || '').includes(PENDING_TAG_INDEX));
186
+ }
187
+
188
+ function mongoStore() {
189
+ return {
190
+ backend: 'mongo',
191
+ touches() { return 0; },
192
+ async snapshot() {
193
+ const { keys, hist } = await mongoCols();
194
+ const working = await readWorking(keys);
195
+ const histDocs = await hist.find({ kind: 'history' }).toArray();
196
+ return {
197
+ keys: working.keys,
198
+ sessions: working.sessions,
199
+ history: histDocs.map((d) => { const h = { ...d }; delete h._id; return h; })
200
+ };
201
+ },
202
+ async mutate(fn) {
203
+ const { keys, hist } = await mongoCols();
204
+ for (let attempt = 0; attempt < 8; attempt++) {
205
+ const snap = await readWorking(keys);
206
+ const beforeKeys = clone(snap.keys);
207
+ const beforeSessions = clone(snap.sessions);
208
+ const changed = await fn(snap);
209
+ if (changed === false) return { written: false };
210
+ const seen = new Set();
211
+ for (const row of Object.values(beforeKeys)) {
212
+ for (const ev of (row && row.history) || []) if (ev && ev.event_id) seen.add(ev.event_id);
213
+ }
214
+ const events = [];
215
+ for (const [id, row] of Object.entries(snap.keys || {})) {
216
+ for (const ev of (row && row.history) || []) {
217
+ if (!ev) continue;
218
+ if (!ev.event_id) ev.event_id = `h:${id}:${crypto.randomBytes(16).toString('hex')}`;
219
+ if (seen.has(ev.event_id)) continue;
220
+ seen.add(ev.event_id);
221
+ events.push({ key_id: id, ev });
222
+ }
223
+ }
224
+ const keyOps = [];
225
+ for (const [id, row] of Object.entries(snap.keys || {})) {
226
+ const prev = beforeKeys[id];
227
+ if (prev && sameDoc(prev, row)) continue;
228
+ keyOps.push({ id, prev: prev || null, next: row, isNew: !prev });
229
+ }
230
+ const sessionOps = [];
231
+ const beforeByOpen = new Map((beforeSessions || []).filter((s) => s && s.open_id).map((s) => [s.open_id, s]));
232
+ for (const s of snap.sessions || []) {
233
+ if (!s || !s.open_id) continue;
234
+ const prev = beforeByOpen.get(s.open_id) || null;
235
+ if (prev && sameDoc(prev, s)) continue;
236
+ sessionOps.push({ id: s.open_id, prev, next: s, isNew: !prev });
237
+ }
238
+ let conflict = false;
239
+ for (const op of keyOps) {
240
+ if (!op.isNew || conflict) continue;
241
+ const body = { ...op.next };
242
+ delete body._id;
243
+ delete body.rev;
244
+ try {
245
+ await keys.insertOne({ _id: `key:${op.id}`, ...body, kind: 'key', key_id: op.id, rev: 1 });
246
+ } catch (err) {
247
+ if (pendingTagClash(err)) { conflict = true; break; }
248
+ throw err;
249
+ }
250
+ }
251
+ const opening = [];
252
+ if (!conflict) {
253
+ for (const op of sessionOps) {
254
+ if (!op.isNew) continue;
255
+ const body = { ...op.next };
256
+ delete body._id;
257
+ delete body.rev;
258
+ const asOpening = body.state === 'live';
259
+ try {
260
+ await keys.insertOne({ _id: `open:${op.id}`, ...body, state: asOpening ? 'opening' : body.state, kind: 'session', open_id: op.id, rev: 1 });
261
+ } catch (err) {
262
+ if (pendingTagClash(err)) { conflict = true; break; }
263
+ throw err;
264
+ }
265
+ if (asOpening) opening.push({ id: op.id, keyId: body.key_id });
266
+ }
267
+ }
268
+ const applied = [];
269
+ if (!conflict) {
270
+ for (const op of keyOps) {
271
+ if (op.isNew) continue;
272
+ const docId = `key:${op.id}`;
273
+ const ok = await casUpdate(keys, docId, op.prev, op.next, { kind: 'key', key_id: op.id });
274
+ if (!ok) { conflict = true; break; }
275
+ applied.push({ id: docId, prev: op.prev });
276
+ }
277
+ }
278
+ if (!conflict) {
279
+ for (const op of sessionOps) {
280
+ if (op.isNew) continue;
281
+ const docId = `open:${op.id}`;
282
+ const ok = await casUpdate(keys, docId, op.prev, op.next, { kind: 'session', open_id: op.id });
283
+ if (!ok) { conflict = true; break; }
284
+ applied.push({ id: docId, prev: op.prev });
285
+ }
286
+ }
287
+ if (conflict) {
288
+ for (const done of applied.reverse()) await revertUpdate(keys, done.id, done.prev);
289
+ continue;
290
+ }
291
+ let flipFailed = false;
292
+ for (const op of opening) {
293
+ const still = await keys.findOne({ _id: `key:${op.keyId}`, live_session_id: op.id });
294
+ if (!still) { flipFailed = true; break; }
295
+ const res = await keys.updateOne({ _id: `open:${op.id}`, state: 'opening' }, { $set: { state: 'live' }, $inc: { rev: 1 } });
296
+ if (res.matchedCount !== 1) { flipFailed = true; break; }
297
+ }
298
+ if (flipFailed) {
299
+ let owned = true;
300
+ for (const done of applied) {
301
+ const cur = await keys.findOne({ _id: done.id });
302
+ if (!cur || cur.rev !== revOf(done.prev) + 1) owned = false;
303
+ }
304
+ if (!owned) throw new Error('superseded by a concurrent open; this open was not made live.');
305
+ for (const done of applied.reverse()) await revertUpdate(keys, done.id, done.prev);
306
+ continue;
307
+ }
308
+ for (const { key_id, ev } of events) {
309
+ await hist.insertOne({ kind: 'history', key_id, ...ev, _id: ev.event_id });
310
+ }
311
+ return { written: true };
312
+ }
313
+ throw new Error('sub-key store conflict. Nothing was written.');
314
+ },
315
+ async sweepFlagOff(note) {
316
+ const client = await acquireSharedClient(MONGODB_URI, { serverSelectionTimeoutMS: 8000, maxPoolSize: 10 });
317
+ const db = client.db(MONGODB_DB);
318
+ const listed = await db.listCollections().toArray();
319
+ if (!listed.some((c) => c.name === SUBKEYS_COLLECTION)) return 0;
320
+ const keys = db.collection(SUBKEYS_COLLECTION);
321
+ const hist = db.collection(SUBKEYS_HISTORY_COLLECTION);
322
+ const sessions = await keys.find({ kind: 'session', state: { $in: ['live', 'opening'] } }).toArray();
323
+ for (const doc of sessions) if (doc.session_id && note) note(doc.session_id);
324
+ const at = new Date().toISOString();
325
+ const put = async (event_id, doc) => {
326
+ try { await hist.insertOne({ _id: event_id, kind: 'history', ...doc, event_id }); }
327
+ catch (err) { if (!err || err.code !== 11000) throw err; }
328
+ };
329
+ let ended = 0;
330
+ for (const doc of sessions) {
331
+ await put(`h:flag_off:${doc.open_id}`, { key_id: doc.key_id || null, event: 'session_ended', at, reason: 'flag_off', session_id: doc.session_id, open_id: doc.open_id });
332
+ const res = await keys.updateOne({ _id: doc._id, state: { $in: ['live', 'opening'] } }, { $set: { state: 'ended', end_reason: 'flag_off', ended_at: at }, $inc: { rev: 1 } });
333
+ if (res.matchedCount !== 1) continue;
334
+ if (doc.key_id && doc.open_id) await keys.updateOne({ _id: `key:${doc.key_id}`, live_session_id: doc.open_id }, { $set: { live_session_id: null, live_set_at: null }, $inc: { rev: 1 } });
335
+ ended += 1;
336
+ }
337
+ return ended;
338
+ },
339
+ async touchLastUsed(keyId, iso) {
340
+ const { keys } = await mongoCols();
341
+ const res = await keys.updateOne({ _id: `key:${keyId}`, kind: 'key' }, { $set: { last_used: iso } });
342
+ return res.matchedCount > 0;
343
+ }
344
+ };
345
+ }
346
+
347
+ export async function sweepFlagOffLiveSessions(note) {
348
+ if (setting('SUBKEYS') === '1' || (!testClientActive() && !MONGODB_URI)) return 0;
349
+ return mongoStore().sweepFlagOff(note);
350
+ }
351
+
352
+ /** An attached store, else the shared Mongo client. No process-memory fallback. */
353
+ export async function storeFor(deps) {
354
+ if (deps && deps.subkeys && typeof deps.subkeys.mutate === 'function') return deps.subkeys;
355
+ if (testClientActive() || MONGODB_URI) return mongoStore();
356
+ return null;
357
+ }