S00-004: Record blocked task and remediation handoff
Changes: Preserve incomplete work and blocker metadata for S00-004. This checkpoint does not mark implementation accepted. Validation: Session ended with status blocked. Blocker: Agent timed out; process terminated before escalation Task: S00-004 Agent: auto-crypto-8bf3dcf711b9 Role: crypto Action: claim Status: blocked Task title: Проверить browser OMEMO backend
This commit is contained in:
1 parent
3784fa7b99
commit
71675b1cc0
16 files changed
+711
-14
No files matched your search
+49
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "0003-crypto-library",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "0003-crypto-library",
|
||||||
|
"dependencies": {
|
||||||
|
"@signalapp/libsignal-client": "0.105.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@signalapp/libsignal-client": {
|
||||||
|
"version": "0.105.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.105.0.tgz",
|
||||||
|
"integrity": "sha512-6H7aQ6/grE6A/wG3nAm+c7aMWhj472/wtbTnNzb9z2KEPZ933PwYGi93zy6Pm63Mxd7SiOSfoa4gSTr++F44UQ==",
|
||||||
|
"license": "AGPL-3.0-only",
|
||||||
|
"dependencies": {
|
||||||
|
"node-gyp-build": "^4.8.0",
|
||||||
|
"type-fest": "^4.26.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-gyp-build": {
|
||||||
|
"version": "4.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||||
|
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"node-gyp-build": "bin.js",
|
||||||
|
"node-gyp-build-optional": "optional.js",
|
||||||
|
"node-gyp-build-test": "build-test.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "4.41.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||||
|
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
|
||||||
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "0003-crypto-library",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24.20.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@signalapp/libsignal-client": "0.105.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// Focused known-answer smoke for an audited OMEMO session/ratchet engine.
|
||||||
|
// Uses Signal libsignal-client (@signalapp/libsignal-client), NOT a hand-written
|
||||||
|
// ratchet. Store semantics mirror Signal's InMemorySignalProtocolStore test
|
||||||
|
// stores. Verifies real end-to-end session encryption and session-record
|
||||||
|
// serialization (persistence vector) without authoring any cryptographic
|
||||||
|
// primitive. Runs on the Node distribution (prebuilt native addon).
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const lib = require('@signalapp/libsignal-client');
|
||||||
|
|
||||||
|
const {
|
||||||
|
Aci,
|
||||||
|
ProtocolAddress,
|
||||||
|
PrivateKey,
|
||||||
|
PreKeyRecord,
|
||||||
|
SignedPreKeyRecord,
|
||||||
|
KyberPreKeyRecord,
|
||||||
|
KEMKeyPair,
|
||||||
|
PreKeyBundle,
|
||||||
|
SessionRecord,
|
||||||
|
IdentityChange,
|
||||||
|
CiphertextMessageType,
|
||||||
|
processPreKeyBundle,
|
||||||
|
signalEncrypt,
|
||||||
|
signalDecrypt,
|
||||||
|
signalDecryptPreKey,
|
||||||
|
CiphertextMessage,
|
||||||
|
IdentityKeyPair,
|
||||||
|
ServerCertificate,
|
||||||
|
SenderCertificate,
|
||||||
|
ContentHint,
|
||||||
|
SignalMessage: SignalLibSignalMessage,
|
||||||
|
PreKeySignalMessage: SignalLibPreKey,
|
||||||
|
sealedSenderEncryptMessage,
|
||||||
|
sealedSenderDecryptMessage,
|
||||||
|
initLogger,
|
||||||
|
LogLevel,
|
||||||
|
} = lib;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const aliceAci = Aci.fromUuid('9d0652a3-dcc3-4d11-975f-74d61598733f');
|
||||||
|
const bobAci = Aci.fromUuid('796abedb-ca4e-4f18-8803-1fde5b921f9f');
|
||||||
|
const aliceAddr = ProtocolAddress.new(aliceAci, 1);
|
||||||
|
const bobAddr = ProtocolAddress.new(bobAci, 3);
|
||||||
|
|
||||||
|
function inMemorySessionStore() {
|
||||||
|
const state = new Map();
|
||||||
|
return {
|
||||||
|
async saveSession(name, record) {
|
||||||
|
state.set(`${name.name()}::${name.deviceId()}`, record.serialize());
|
||||||
|
},
|
||||||
|
async getSession(name) {
|
||||||
|
const s = state.get(`${name.name()}::${name.deviceId()}`);
|
||||||
|
return s ? SessionRecord.deserialize(s) : null;
|
||||||
|
},
|
||||||
|
async getExistingSessions(addresses) {
|
||||||
|
return addresses.map((a) => {
|
||||||
|
const s = state.get(`${a.name()}::${a.deviceId()}`);
|
||||||
|
if (!s) throw new Error('no session for ' + a.toString());
|
||||||
|
return SessionRecord.deserialize(s);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async _storedSessions() {
|
||||||
|
return Array.from(state.values());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function inMemoryIdentityKeyStore() {
|
||||||
|
const idKeys = new Map();
|
||||||
|
const identityKey = PrivateKey.generate();
|
||||||
|
return {
|
||||||
|
async getIdentityKey() { return identityKey; },
|
||||||
|
async getIdentityKeyPair() {
|
||||||
|
return new IdentityKeyPair(identityKey.getPublicKey(), identityKey);
|
||||||
|
},
|
||||||
|
async getLocalRegistrationId() { return 5; },
|
||||||
|
async isTrustedIdentity(name, key) {
|
||||||
|
const current = idKeys.get(`${name.name()}::${name.deviceId()}`);
|
||||||
|
return current ? current.equals(key) : true;
|
||||||
|
},
|
||||||
|
async saveIdentity(name, key) {
|
||||||
|
const current = idKeys.get(`${name.name()}::${name.deviceId()}`);
|
||||||
|
idKeys.set(`${name.name()}::${name.deviceId()}`, key);
|
||||||
|
const changed = !(current && current.equals(key));
|
||||||
|
return changed ? IdentityChange.ReplacedExisting : IdentityChange.NewOrUnchanged;
|
||||||
|
},
|
||||||
|
async getIdentity(name) {
|
||||||
|
return idKeys.get(`${name.name()}::${name.deviceId()}`) ?? null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function inMemoryPreKeyStore() {
|
||||||
|
const state = new Map();
|
||||||
|
return {
|
||||||
|
async savePreKey(id, record) { state.set(id, record.serialize()); },
|
||||||
|
async getPreKey(id) {
|
||||||
|
const r = state.get(id);
|
||||||
|
if (!r) throw new Error('pre-key ' + id + ' not found');
|
||||||
|
return PreKeyRecord.deserialize(r);
|
||||||
|
},
|
||||||
|
async removePreKey(id) { state.delete(id); },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function inMemorySignedPreKeyStore() {
|
||||||
|
const state = new Map();
|
||||||
|
return {
|
||||||
|
async saveSignedPreKey(id, record) { state.set(id, record.serialize()); },
|
||||||
|
async getSignedPreKey(id) {
|
||||||
|
const r = state.get(id);
|
||||||
|
if (!r) throw new Error('signed pre-key ' + id + ' not found');
|
||||||
|
return SignedPreKeyRecord.deserialize(r);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function inMemoryKyberPreKeyStore() {
|
||||||
|
const state = new Map();
|
||||||
|
const used = new Set();
|
||||||
|
return {
|
||||||
|
async saveKyberPreKey(id, record) { state.set(id, record.serialize()); },
|
||||||
|
async getKyberPreKey(id) {
|
||||||
|
const r = state.get(id);
|
||||||
|
if (!r) throw new Error('kyber pre-key ' + id + ' not found');
|
||||||
|
return KyberPreKeyRecord.deserialize(r);
|
||||||
|
},
|
||||||
|
async markKyberPreKeyUsed(id) { used.add(id); },
|
||||||
|
async hasKyberPreKeyBeenUsed(id) { return used.has(id); },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rawDecryptPreKey(preKeyMessage, addr, localAddr, sessionStore, identityStore, preK, sPreK, kyber) {
|
||||||
|
return signalDecryptPreKey(preKeyMessage, addr, localAddr, sessionStore, identityStore, preK, sPreK, kyber);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
initLogger(LogLevel.Trace, (level, target, file, line, message) => {
|
||||||
|
if (level >= LogLevel.Warn || /decrypt|pre.?key|session|cipher/i.test(message)) {
|
||||||
|
console.log(`[log:${level}]`, message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const aliceStore = inMemorySessionStore();
|
||||||
|
const bobStore = inMemorySessionStore();
|
||||||
|
const aliceKeys = inMemoryIdentityKeyStore();
|
||||||
|
const bobKeys = inMemoryIdentityKeyStore();
|
||||||
|
const bPreK = inMemoryPreKeyStore();
|
||||||
|
const bSPreK = inMemorySignedPreKeyStore();
|
||||||
|
const bKyberStore = inMemoryKyberPreKeyStore();
|
||||||
|
|
||||||
|
const bPreKey = PrivateKey.generate();
|
||||||
|
const bSPreKey = PrivateKey.generate();
|
||||||
|
const bIdentityKey = await bobKeys.getIdentityKey();
|
||||||
|
const bPreKeyId = 31337;
|
||||||
|
const bSignedPreKeyId = 22;
|
||||||
|
const bSignedPreKeySig = bIdentityKey.sign(bSPreKey.getPublicKey().serialize());
|
||||||
|
const bKyberPrekeyId = 777;
|
||||||
|
const bKyberKeyPair = await KEMKeyPair.generate();
|
||||||
|
const bKyberPrekeySignature = bIdentityKey.sign(bKyberKeyPair.getPublicKey().serialize());
|
||||||
|
|
||||||
|
const bPreKeyBundle = PreKeyBundle.new(
|
||||||
|
5, bAddrDevice(), bPreKeyId, bPreKey.getPublicKey(),
|
||||||
|
bSignedPreKeyId, bSPreKey.getPublicKey(), bSignedPreKeySig,
|
||||||
|
bIdentityKey.getPublicKey(),
|
||||||
|
bKyberPrekeyId, bKyberKeyPair.getPublicKey(), bKyberPrekeySignature,
|
||||||
|
);
|
||||||
|
|
||||||
|
await bPreK.savePreKey(bPreKeyId, PreKeyRecord.new(bPreKeyId, bPreKey.getPublicKey(), bPreKey));
|
||||||
|
await bSPreK.saveSignedPreKey(bSignedPreKeyId, SignedPreKeyRecord.new(bSignedPreKeyId, 42, bSPreKey.getPublicKey(), bSPreKey, bSignedPreKeySig));
|
||||||
|
await bKyberStore.saveKyberPreKey(bKyberPrekeyId, KyberPreKeyRecord.new(bKyberPrekeyId, 42, bKyberKeyPair, bKyberPrekeySignature));
|
||||||
|
|
||||||
|
// Alice (local) processes Bob's bundle to prepare a session to Bob.
|
||||||
|
await processPreKeyBundle(bPreKeyBundle, bobAddr, aliceAddr, aliceStore, aliceKeys, now);
|
||||||
|
|
||||||
|
// --- Primary canonical known-answer: sealed-sender establishes the session. ---
|
||||||
|
const aIdentityKey = await aliceKeys.getIdentityKey();
|
||||||
|
const trustRoot = PrivateKey.generate();
|
||||||
|
const serverKey = PrivateKey.generate();
|
||||||
|
const serverCert = ServerCertificate.new(1, serverKey.getPublicKey(), trustRoot);
|
||||||
|
const senderCert = SenderCertificate.new(
|
||||||
|
aliceAci.toString(), null, aliceAddr.deviceId(),
|
||||||
|
aIdentityKey.getPublicKey(), now.getTime() + 100000, serverCert, serverKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
const aPlaintext = Buffer.from('known vector: OMEMO session smoke', 'utf8');
|
||||||
|
const aSealed = await sealedSenderEncryptMessage(aPlaintext, bobAddr, senderCert, aliceStore, aliceKeys);
|
||||||
|
const bResult = await sealedSenderDecryptMessage(
|
||||||
|
aSealed, trustRoot.getPublicKey(), now.getTime(),
|
||||||
|
null, bobAci.toString(), bobAddr.deviceId(),
|
||||||
|
bobStore, bobKeys, bPreK, bSPreK, bKyberStore,
|
||||||
|
);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
new Uint8Array(bResult.message()),
|
||||||
|
new Uint8Array(aPlaintext),
|
||||||
|
'sealed-sender session known plaintext round-trips (session established)',
|
||||||
|
);
|
||||||
|
|
||||||
|
// A must now hold a session to Bob.
|
||||||
|
const aliceSession = await aliceStore.getSession(bobAddr);
|
||||||
|
assert.ok(aliceSession instanceof SessionRecord, 'alice holds a session record to bob after encryption');
|
||||||
|
const aliceSessionBytes = aliceSession.serialize();
|
||||||
|
assert.ok(aliceSessionBytes.length > 0, 'alice session record serializes');
|
||||||
|
|
||||||
|
// Note (documented, not a failure): in this libsignal release a separate raw
|
||||||
|
// `signalEncrypt` call toward Bob re-initiates a PreKey flow (native log:
|
||||||
|
// "Building PreKeyWhisperMessage ... with preKeyId") rather than continuing a
|
||||||
|
// Whisper chain. That is a session-management integration detail for the
|
||||||
|
// OMEMO adapter layer, not a home-written ratchet. We rely on the verified
|
||||||
|
// sealed-sender session path for the known-answer result above.
|
||||||
|
|
||||||
|
// Persistence vector: Bob established and stores a session record; it
|
||||||
|
// serializes into non-empty bytes and reloads byte-for-byte.
|
||||||
|
const stored = await bobStore._storedSessions();
|
||||||
|
assert.ok(stored.length === 1, 'bob keeps exactly one established session record');
|
||||||
|
const storedBytes = new Uint8Array(stored[0]);
|
||||||
|
assert.ok(storedBytes.length > 0, 'session record serializes to non-empty bytes');
|
||||||
|
const reloaded = SessionRecord.deserialize(storedBytes);
|
||||||
|
assert.ok(reloaded instanceof SessionRecord, 'session record deserializes');
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(reloaded.serialize()).equals(Buffer.from(storedBytes)),
|
||||||
|
true,
|
||||||
|
'session round-trips byte-for-byte',
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('KAS_OK known plaintext:', Buffer.from(bResult.message()).toString('utf8'));
|
||||||
|
console.log('KAS_OK established via sealed-sender PreKey session');
|
||||||
|
console.log('KAS_OK bob stored session bytes:', storedBytes.length);
|
||||||
|
console.log('SMOKE_PASS');
|
||||||
|
}
|
||||||
|
|
||||||
|
function bAddrDevice() { return 3; }
|
||||||
|
|
||||||
|
main().catch((e) => { console.error('SMOKE_FAIL', (e && e.stack) || e); process.exit(1); });
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Focused check 2: unsupported format recorded as blocker, not success.
|
||||||
|
// Verifies the honest boundary of what @signalapp/libsignal-client provides for
|
||||||
|
// a Web (browser/worker) deployment and for the OMEMO/OMEMO2 wire format.
|
||||||
|
// This script deliberately does NOT claim success for anything it cannot show.
|
||||||
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
||||||
|
import { join, dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const pkgDir = join(here, '..', 'node_modules', '@signalapp', 'libsignal-client');
|
||||||
|
|
||||||
|
function walkWasm(dir, acc = []) {
|
||||||
|
for (const e of readdirSync(dir)) {
|
||||||
|
const p = join(dir, e);
|
||||||
|
if (statSync(p).isDirectory()) walkWasm(p, acc);
|
||||||
|
else if (e.endsWith('.wasm')) acc.push(p);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wasmFiles = existsSync(pkgDir) ? walkWasm(pkgDir) : null;
|
||||||
|
const prebuildDir = join(pkgDir, 'prebuilds');
|
||||||
|
const prebuilds = existsSync(prebuildDir) ? readdirSync(prebuildDir) : [];
|
||||||
|
|
||||||
|
console.log('pkgPresent:', existsSync(pkgDir));
|
||||||
|
console.log('wasmFiles:', wasmFiles ? wasmFiles.length : 'n/a');
|
||||||
|
console.log('prebuilds:', prebuilds.join(', '));
|
||||||
|
|
||||||
|
if (!wasmFiles || wasmFiles.length === 0) {
|
||||||
|
// This is an unsupported backend for a browser worker, not a success.
|
||||||
|
const msg =
|
||||||
|
'UNSUPPORTED_BLOCKER: no .wasm asset ships in @signalapp/libsignal-client; ' +
|
||||||
|
'the distribution is a Node native addon (prebuilds/*/*.node). A browser ' +
|
||||||
|
'worker/WASM deployment is NOT established by this npm package and must be ' +
|
||||||
|
'treated as a blocker until a verified browser/WASM distribution is found.';
|
||||||
|
console.log(msg);
|
||||||
|
process.exit(2);
|
||||||
|
} else {
|
||||||
|
console.log('wasm present (unexpected here)');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Зафиксировать Web scope, browser targets и проверенные кандидаты, не устанавливая непроверенный framework по догадке.
|
Зафиксировать Web scope, browser targets и проверенные кандидаты, не устанавливая непроверенный framework по догадке.
|
||||||
|
|
||||||
Задач: 20; entry: нет; exit: S00-010.
|
Задач: 21; entry: нет; exit: S00-010.
|
||||||
|
|
||||||
## [S01: Предметные контракты](stages/S01.md)
|
## [S01: Предметные контракты](stages/S01.md)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# Матрица задач
|
# Матрица задач
|
||||||
|
|
||||||
Generated; state revision **54**. Canonical status: state/tasks.json.
|
Generated; state revision **57**. Canonical status: state/tasks.json.
|
||||||
|
|
||||||
| ID | Этап | Оператор | Reviewer | Статус | Owner | Зависимости | Задача |
|
| ID | Этап | Оператор | Reviewer | Статус | Owner | Зависимости | Задача |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| [S00-001](tasks/S00-001.md) | S00 | orchestrator | security | done | auto-orchestrator-48aab142ecde | | Утвердить Web-профиль |
|
| [S00-001](tasks/S00-001.md) | S00 | orchestrator | security | done | auto-orchestrator-48aab142ecde | | Утвердить Web-профиль |
|
||||||
| [S00-002](tasks/S00-002.md) | S00 | orchestrator | security | done | auto-orchestrator-68a05c587e33 | S00-001, S00-011, S00-012 | Выбрать framework и build runtime |
|
| [S00-002](tasks/S00-002.md) | S00 | orchestrator | security | done | auto-orchestrator-68a05c587e33 | S00-001, S00-011, S00-012 | Выбрать framework и build runtime |
|
||||||
| [S00-003](tasks/S00-003.md) | S00 | protocol | qa | blocked | auto-protocol-59f4d359b339 | S00-001, S00-002, S00-017, S00-018, S00-019, S00-020 | Проверить XMPP WSS/auth библиотеку |
|
| [S00-003](tasks/S00-003.md) | S00 | protocol | qa | blocked | auto-protocol-59f4d359b339 | S00-001, S00-002, S00-017, S00-018, S00-019, S00-020 | Проверить XMPP WSS/auth библиотеку |
|
||||||
| [S00-004](tasks/S00-004.md) | S00 | crypto | security | todo | | S00-001, S00-002 | Проверить browser OMEMO backend |
|
| [S00-004](tasks/S00-004.md) | S00 | crypto | security | blocked | auto-crypto-8bf3dcf711b9 | S00-001, S00-002, S00-021 | Проверить browser OMEMO backend |
|
||||||
| [S00-005](tasks/S00-005.md) | S00 | storage | qa | todo | | S00-002 | Выбрать IndexedDB wrapper и migration policy |
|
| [S00-005](tasks/S00-005.md) | S00 | storage | qa | todo | | S00-002 | Выбрать IndexedDB wrapper и migration policy |
|
||||||
| [S00-006](tasks/S00-006.md) | S00 | orchestrator | security | todo | | S00-002 | Зафиксировать browser/reference matrix |
|
| [S00-006](tasks/S00-006.md) | S00 | orchestrator | security | todo | | S00-002 | Зафиксировать browser/reference matrix |
|
||||||
| [S00-007](tasks/S00-007.md) | S00 | security | orchestrator | todo | | S00-004, S00-005 | Утвердить password и crypto unlock модель |
|
| [S00-007](tasks/S00-007.md) | S00 | security | orchestrator | todo | | S00-004, S00-005 | Утвердить password и crypto unlock модель |
|
||||||
@@ -22,7 +22,8 @@ Generated; state revision **54**. Canonical status: state/tasks.json.
|
|||||||
| [S00-017](tasks/S00-017.md) | S00 | protocol | security | done | auto-protocol-599343cd9e3f | S00-001, S00-002 | Establish fail-closed SCRAM server-final validation |
|
| [S00-017](tasks/S00-017.md) | S00 | protocol | security | done | auto-protocol-599343cd9e3f | S00-001, S00-002 | Establish fail-closed SCRAM server-final validation |
|
||||||
| [S00-018](tasks/S00-018.md) | S00 | protocol | security | done | auto-protocol-89fe6563092c | S00-001, S00-002, S00-017 | Validate bounded standalone XML ingress for the XMPP candidate |
|
| [S00-018](tasks/S00-018.md) | S00 | protocol | security | done | auto-protocol-89fe6563092c | S00-001, S00-002, S00-017 | Validate bounded standalone XML ingress for the XMPP candidate |
|
||||||
| [S00-019](tasks/S00-019.md) | S00 | crypto | security | done | auto-crypto-c68801170fa4 | S00-001, S00-002, S00-017, S00-018 | Validate browser-compatible SCRAM-SHA-512 through a public mechanism API |
|
| [S00-019](tasks/S00-019.md) | S00 | crypto | security | done | auto-crypto-c68801170fa4 | S00-001, S00-002, S00-017, S00-018 | Validate browser-compatible SCRAM-SHA-512 through a public mechanism API |
|
||||||
| [S00-020](tasks/S00-020.md) | S00 | ci-cd | qa | todo | | S00-001, S00-002, S00-017, S00-018, S00-019 | Restore access to the pinned Linux XMPP experiment toolchain |
|
| [S00-020](tasks/S00-020.md) | S00 | ci-cd | qa | in_progress | auto-ci-cd-5a18c8aa1b42 | S00-001, S00-002, S00-017, S00-018, S00-019 | Restore access to the pinned Linux XMPP experiment toolchain |
|
||||||
|
| [S00-021](tasks/S00-021.md) | S00 | crypto | security | todo | | S00-001, S00-002 | Устранить прерывание: Проверить browser OMEMO backend |
|
||||||
| [S00-010](tasks/S00-010.md) | S00 | qa | orchestrator | todo | | S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009 | Gate: Профиль и проверка стека |
|
| [S00-010](tasks/S00-010.md) | S00 | qa | orchestrator | todo | | S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009 | Gate: Профиль и проверка стека |
|
||||||
| [S01-001](tasks/S01-001.md) | S01 | orchestrator | security | todo | | S00-010 | Описать identity/model contracts |
|
| [S01-001](tasks/S01-001.md) | S01 | orchestrator | security | todo | | S00-010 | Описать identity/model contracts |
|
||||||
| [S01-002](tasks/S01-002.md) | S01 | orchestrator | security | todo | | S00-010, S01-001 | Описать async account context |
|
| [S01-002](tasks/S01-002.md) | S01 | orchestrator | security | todo | | S00-010, S01-001 | Описать async account context |
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
- [S00-001](../tasks/S00-001.md): Утвердить Web-профиль — orchestrator; done.
|
- [S00-001](../tasks/S00-001.md): Утвердить Web-профиль — orchestrator; done.
|
||||||
- [S00-002](../tasks/S00-002.md): Выбрать framework и build runtime — orchestrator; done.
|
- [S00-002](../tasks/S00-002.md): Выбрать framework и build runtime — orchestrator; done.
|
||||||
- [S00-003](../tasks/S00-003.md): Проверить XMPP WSS/auth библиотеку — protocol; blocked.
|
- [S00-003](../tasks/S00-003.md): Проверить XMPP WSS/auth библиотеку — protocol; blocked.
|
||||||
- [S00-004](../tasks/S00-004.md): Проверить browser OMEMO backend — crypto; todo.
|
- [S00-004](../tasks/S00-004.md): Проверить browser OMEMO backend — crypto; blocked.
|
||||||
- [S00-005](../tasks/S00-005.md): Выбрать IndexedDB wrapper и migration policy — storage; todo.
|
- [S00-005](../tasks/S00-005.md): Выбрать IndexedDB wrapper и migration policy — storage; todo.
|
||||||
- [S00-006](../tasks/S00-006.md): Зафиксировать browser/reference matrix — orchestrator; todo.
|
- [S00-006](../tasks/S00-006.md): Зафиксировать browser/reference matrix — orchestrator; todo.
|
||||||
- [S00-007](../tasks/S00-007.md): Утвердить password и crypto unlock модель — security; todo.
|
- [S00-007](../tasks/S00-007.md): Утвердить password и crypto unlock модель — security; todo.
|
||||||
@@ -24,7 +24,8 @@
|
|||||||
- [S00-017](../tasks/S00-017.md): Establish fail-closed SCRAM server-final validation — protocol; done.
|
- [S00-017](../tasks/S00-017.md): Establish fail-closed SCRAM server-final validation — protocol; done.
|
||||||
- [S00-018](../tasks/S00-018.md): Validate bounded standalone XML ingress for the XMPP candidate — protocol; done.
|
- [S00-018](../tasks/S00-018.md): Validate bounded standalone XML ingress for the XMPP candidate — protocol; done.
|
||||||
- [S00-019](../tasks/S00-019.md): Validate browser-compatible SCRAM-SHA-512 through a public mechanism API — crypto; done.
|
- [S00-019](../tasks/S00-019.md): Validate browser-compatible SCRAM-SHA-512 through a public mechanism API — crypto; done.
|
||||||
- [S00-020](../tasks/S00-020.md): Restore access to the pinned Linux XMPP experiment toolchain — ci-cd; todo.
|
- [S00-020](../tasks/S00-020.md): Restore access to the pinned Linux XMPP experiment toolchain — ci-cd; in_progress.
|
||||||
|
- [S00-021](../tasks/S00-021.md): Устранить прерывание: Проверить browser OMEMO backend — crypto; todo.
|
||||||
- [S00-010](../tasks/S00-010.md): Gate: Профиль и проверка стека — qa; todo.
|
- [S00-010](../tasks/S00-010.md): Gate: Профиль и проверка стека — qa; todo.
|
||||||
|
|
||||||
## Граница этапа
|
## Граница этапа
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ id,stage,operator,reviewer,status,owner,depends_on,title
|
|||||||
S00-001,S00,orchestrator,security,done,auto-orchestrator-48aab142ecde,,Утвердить Web-профиль
|
S00-001,S00,orchestrator,security,done,auto-orchestrator-48aab142ecde,,Утвердить Web-профиль
|
||||||
S00-002,S00,orchestrator,security,done,auto-orchestrator-68a05c587e33,"S00-001, S00-011, S00-012",Выбрать framework и build runtime
|
S00-002,S00,orchestrator,security,done,auto-orchestrator-68a05c587e33,"S00-001, S00-011, S00-012",Выбрать framework и build runtime
|
||||||
S00-003,S00,protocol,qa,blocked,auto-protocol-59f4d359b339,"S00-001, S00-002, S00-017, S00-018, S00-019, S00-020",Проверить XMPP WSS/auth библиотеку
|
S00-003,S00,protocol,qa,blocked,auto-protocol-59f4d359b339,"S00-001, S00-002, S00-017, S00-018, S00-019, S00-020",Проверить XMPP WSS/auth библиотеку
|
||||||
S00-004,S00,crypto,security,todo,,"S00-001, S00-002",Проверить browser OMEMO backend
|
S00-004,S00,crypto,security,blocked,auto-crypto-8bf3dcf711b9,"S00-001, S00-002, S00-021",Проверить browser OMEMO backend
|
||||||
S00-005,S00,storage,qa,todo,,S00-002,Выбрать IndexedDB wrapper и migration policy
|
S00-005,S00,storage,qa,todo,,S00-002,Выбрать IndexedDB wrapper и migration policy
|
||||||
S00-006,S00,orchestrator,security,todo,,S00-002,Зафиксировать browser/reference matrix
|
S00-006,S00,orchestrator,security,todo,,S00-002,Зафиксировать browser/reference matrix
|
||||||
S00-007,S00,security,orchestrator,todo,,"S00-004, S00-005",Утвердить password и crypto unlock модель
|
S00-007,S00,security,orchestrator,todo,,"S00-004, S00-005",Утвердить password и crypto unlock модель
|
||||||
@@ -17,7 +17,8 @@ S00-016,S00,ci-cd,qa,done,auto-ci-cd-351a0cbbabef,"S00-001, S00-011, S00-013, S0
|
|||||||
S00-017,S00,protocol,security,done,auto-protocol-599343cd9e3f,"S00-001, S00-002",Establish fail-closed SCRAM server-final validation
|
S00-017,S00,protocol,security,done,auto-protocol-599343cd9e3f,"S00-001, S00-002",Establish fail-closed SCRAM server-final validation
|
||||||
S00-018,S00,protocol,security,done,auto-protocol-89fe6563092c,"S00-001, S00-002, S00-017",Validate bounded standalone XML ingress for the XMPP candidate
|
S00-018,S00,protocol,security,done,auto-protocol-89fe6563092c,"S00-001, S00-002, S00-017",Validate bounded standalone XML ingress for the XMPP candidate
|
||||||
S00-019,S00,crypto,security,done,auto-crypto-c68801170fa4,"S00-001, S00-002, S00-017, S00-018",Validate browser-compatible SCRAM-SHA-512 through a public mechanism API
|
S00-019,S00,crypto,security,done,auto-crypto-c68801170fa4,"S00-001, S00-002, S00-017, S00-018",Validate browser-compatible SCRAM-SHA-512 through a public mechanism API
|
||||||
S00-020,S00,ci-cd,qa,todo,,"S00-001, S00-002, S00-017, S00-018, S00-019",Restore access to the pinned Linux XMPP experiment toolchain
|
S00-020,S00,ci-cd,qa,in_progress,auto-ci-cd-5a18c8aa1b42,"S00-001, S00-002, S00-017, S00-018, S00-019",Restore access to the pinned Linux XMPP experiment toolchain
|
||||||
|
S00-021,S00,crypto,security,todo,,"S00-001, S00-002",Устранить прерывание: Проверить browser OMEMO backend
|
||||||
S00-010,S00,qa,orchestrator,todo,,"S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009",Gate: Профиль и проверка стека
|
S00-010,S00,qa,orchestrator,todo,,"S00-001, S00-002, S00-003, S00-004, S00-005, S00-006, S00-007, S00-008, S00-009",Gate: Профиль и проверка стека
|
||||||
S01-001,S01,orchestrator,security,todo,,S00-010,Описать identity/model contracts
|
S01-001,S01,orchestrator,security,todo,,S00-010,Описать identity/model contracts
|
||||||
S01-002,S01,orchestrator,security,todo,,"S00-010, S01-001",Описать async account context
|
S01-002,S01,orchestrator,security,todo,,"S00-010, S01-001",Описать async account context
|
||||||
|
|||||||
|
+51
-1
@@ -323,7 +323,8 @@
|
|||||||
"outcome": "Spike и ADR устанавливают реальную возможность legacy/OMEMO2, worker/wasm и persistence без самодельного ratchet.",
|
"outcome": "Spike и ADR устанавливают реальную возможность legacy/OMEMO2, worker/wasm и persistence без самодельного ratchet.",
|
||||||
"depends_on": [
|
"depends_on": [
|
||||||
"S00-001",
|
"S00-001",
|
||||||
"S00-002"
|
"S00-002",
|
||||||
|
"S00-021"
|
||||||
],
|
],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"ENC-003",
|
"ENC-003",
|
||||||
@@ -1173,6 +1174,55 @@
|
|||||||
],
|
],
|
||||||
"context_budget_chars": 48000
|
"context_budget_chars": 48000
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "S00-021",
|
||||||
|
"stage": "S00",
|
||||||
|
"kind": "blocker",
|
||||||
|
"root_task": "S00-004",
|
||||||
|
"blocker_depth": 1,
|
||||||
|
"unblocks": [
|
||||||
|
"S00-004"
|
||||||
|
],
|
||||||
|
"role": "crypto",
|
||||||
|
"reviewer_role": "security",
|
||||||
|
"title": "Устранить прерывание: Проверить browser OMEMO backend",
|
||||||
|
"outcome": "Устранить одну указанную причину прерывания и доказать готовность исходной задачи к новому запуску; не выполнять соседние задачи.",
|
||||||
|
"depends_on": [
|
||||||
|
"S00-001",
|
||||||
|
"S00-002"
|
||||||
|
],
|
||||||
|
"requirements": [],
|
||||||
|
"scope": [
|
||||||
|
"docs/decisions/0003-crypto-library.md",
|
||||||
|
"experiments/0003-crypto-library/**"
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"docs/WEB_PROFILE.md",
|
||||||
|
"docs/ARCHITECTURE.md",
|
||||||
|
"docs/CONTRACTS.md",
|
||||||
|
"docs/TESTING.md",
|
||||||
|
"roles/crypto.md",
|
||||||
|
"docs/decisions/README.md",
|
||||||
|
"state/blockers/S00-021.md"
|
||||||
|
],
|
||||||
|
"spec_sections": [],
|
||||||
|
"ui_sections": [],
|
||||||
|
"steps": [
|
||||||
|
"Прочитать blocker report и handoff.",
|
||||||
|
"Воспроизвести конкретную причину.",
|
||||||
|
"Исправить только локальную причину в scope или уточнить внешний input.",
|
||||||
|
"Выполнить focused проверку и submit."
|
||||||
|
],
|
||||||
|
"acceptance": [
|
||||||
|
"Причина воспроизводимо устранена.",
|
||||||
|
"Исходная задача не завершена обходом требований."
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
"Повторить сценарий прерывания.",
|
||||||
|
"Проверить отсутствие изменений вне scope."
|
||||||
|
],
|
||||||
|
"context_budget_chars": 48000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "S00-010",
|
"id": "S00-010",
|
||||||
"stage": "S00",
|
"stage": "S00",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
- Этап: S00
|
- Этап: S00
|
||||||
- Оператор: crypto
|
- Оператор: crypto
|
||||||
- Reviewer: security
|
- Reviewer: security
|
||||||
- Зависимости: S00-001, S00-002
|
- Зависимости: S00-001, S00-002, S00-021
|
||||||
- Требования: ENC-003, ENC-009, ENC-011
|
- Требования: ENC-003, ENC-009, ENC-011
|
||||||
|
|
||||||
## Один результат
|
## Один результат
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# S00-021: Устранить прерывание: Проверить browser OMEMO backend
|
||||||
|
|
||||||
|
- Remediation для: S00-004; root: S00-004; depth: 1
|
||||||
|
|
||||||
|
> Generated from planning/tasks.json; status is in state/tasks.json.
|
||||||
|
|
||||||
|
- Этап: S00
|
||||||
|
- Оператор: crypto
|
||||||
|
- Reviewer: security
|
||||||
|
- Зависимости: S00-001, S00-002
|
||||||
|
- Требования: инженерная подготовка
|
||||||
|
|
||||||
|
## Один результат
|
||||||
|
|
||||||
|
Устранить одну указанную причину прерывания и доказать готовность исходной задачи к новому запуску; не выполнять соседние задачи.
|
||||||
|
|
||||||
|
## Разрешённые пути
|
||||||
|
|
||||||
|
- `docs/decisions/0003-crypto-library.md`
|
||||||
|
- `experiments/0003-crypto-library/**`
|
||||||
|
|
||||||
|
Не изменять другие modules, shared contracts или spec snapshots без изменения плана. Stem без расширения разрешает принятые stack-specific extensions и focused test рядом.
|
||||||
|
|
||||||
|
## Прочитать
|
||||||
|
|
||||||
|
- docs/WEB_PROFILE.md
|
||||||
|
- docs/ARCHITECTURE.md
|
||||||
|
- docs/CONTRACTS.md
|
||||||
|
- docs/TESTING.md
|
||||||
|
- roles/crypto.md
|
||||||
|
- docs/decisions/README.md
|
||||||
|
- state/blockers/S00-021.md
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Прочитать blocker report и handoff.
|
||||||
|
2. Воспроизвести конкретную причину.
|
||||||
|
3. Исправить только локальную причину в scope или уточнить внешний input.
|
||||||
|
4. Выполнить focused проверку и submit.
|
||||||
|
|
||||||
|
## Приёмка
|
||||||
|
|
||||||
|
1. Причина воспроизводимо устранена.
|
||||||
|
2. Исходная задача не завершена обходом требований.
|
||||||
|
|
||||||
|
## Focused checks
|
||||||
|
|
||||||
|
1. Повторить сценарий прерывания.
|
||||||
|
2. Проверить отсутствие изменений вне scope.
|
||||||
|
|
||||||
|
## Блокер и завершение запуска
|
||||||
|
|
||||||
|
Для всех ролей scope — только эта задача. При блокере создать issue JSON по state/ISSUE_TEMPLATE.json, вызвать escalate с текущим ID/role/agent, проверить status=blocked и resolution_task, затем немедленно завершить запуск. Не решать blocker и не выбирать следующую задачу самому. Новый агент назначается runner. Правки metadata через CLI — единственное исключение scope.
|
||||||
|
|
||||||
|
## Передача
|
||||||
|
|
||||||
|
Сохранить evidence/handoff; submit → review и завершить запуск. Reviewer также выполняет только эту задачу, включая обязательное escalate при блокере.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Blocker S00-021
|
||||||
|
|
||||||
|
- Original task: S00-004
|
||||||
|
- Root task: S00-004
|
||||||
|
- Reporter: auto-crypto-8bf3dcf711b9
|
||||||
|
- Role: crypto
|
||||||
|
- Original owner: auto-crypto-8bf3dcf711b9
|
||||||
|
- Waits for user/external condition: False
|
||||||
|
|
||||||
|
## Cause
|
||||||
|
|
||||||
|
Agent timed out; process terminated before escalation
|
||||||
|
|
||||||
|
## One remediation outcome
|
||||||
|
|
||||||
|
Устранить одну указанную причину прерывания и доказать готовность исходной задачи к новому запуску; не выполнять соседние задачи.
|
||||||
|
|
||||||
|
## Evidence / reproduction
|
||||||
|
|
||||||
|
See original handoff and task evidence.
|
||||||
|
|
||||||
|
The reporting agent must stop now. A new agent implements only S00-021.
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# S00-004 - Browser OMEMO backend spike - Evidence
|
||||||
|
|
||||||
|
- Agent: `auto-crypto-8bf3dcf711b9`
|
||||||
|
- Role: crypto
|
||||||
|
- Task: S00-004 "Проверить browser OMEMO backend"
|
||||||
|
- Date-of-run: 2026-10-15
|
||||||
|
|
||||||
|
## 1. Known-answer session/ratchet + persistence probe (PASS)
|
||||||
|
|
||||||
|
Command and result:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd experiments/0003-crypto-library
|
||||||
|
node session-probe/smoke.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
Actual output (native libsignal trace + KAS lines):
|
||||||
|
|
||||||
|
```
|
||||||
|
[log:3] Building PreKeyWhisperMessage for: 796abedb-...-4214?.3 with preKeyId: 31337 (session created at 1792052846)
|
||||||
|
[log:3] decrypted PreKey message from <ACI:9d0652a3-...>.1 with current session state ...
|
||||||
|
KAS_OK known plaintext: known vector: OMEMO session smoke
|
||||||
|
KAS_OK established via sealed-sender PreKey session
|
||||||
|
KAS_OK bob stored session bytes: 631
|
||||||
|
SMOKE_PASS
|
||||||
|
exit code: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
What is shown: using the canonical real library API
|
||||||
|
(`sealedSenderEncryptMessage`/`sealedSenderDecryptMessage`) an independent
|
||||||
|
A/B pair established a PreKey session; the exact known plaintext string
|
||||||
|
(`known vector: OMEMO session smoke`) round-tripped; the decrypted-stored
|
||||||
|
session record serialized to 631 non-empty bytes and reloaded byte-for-byte
|
||||||
|
via `SessionRecord.deserialize`.
|
||||||
|
|
||||||
|
Verified capability: session/ratchet establishment and session-record
|
||||||
|
persistence on a real, unmodified, audited Signal session library. **No
|
||||||
|
homegrown ratchet.**
|
||||||
|
|
||||||
|
## 2. Unsupported format/backend probe (BLOCKER, exit 2)
|
||||||
|
|
||||||
|
Command and result:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd experiments/0003-crypto-library
|
||||||
|
node session-probe/wasm-unsupported.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
Actual output:
|
||||||
|
|
||||||
|
```
|
||||||
|
pkgPresent: true
|
||||||
|
wasmFiles: 0
|
||||||
|
prebuilds: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-arm64, win32-x64
|
||||||
|
UNSUPPORTED_BLOCKER: no .wasm asset ships in @signalapp/libsignal-client; the
|
||||||
|
distribution is a Node native addon (prebuilds/*/*.node). A browser worker/WASM
|
||||||
|
deployment is NOT established by this npm package ...
|
||||||
|
exit code: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
What is shown: the only maintained, installable, audited session library in
|
||||||
|
this registry (`@signalapp/libsignal-client@0.105.0`, AGPL-3.0-only) ships as a
|
||||||
|
Node native addon and contains no WebAssembly asset. It therefore cannot execute
|
||||||
|
in a browser worker as required by the Web client-endpoint.
|
||||||
|
|
||||||
|
## 3. OMEMO2 wire-format candidate search (no maintained package)
|
||||||
|
|
||||||
|
Registry queries (via proxy, real responses):
|
||||||
|
|
||||||
|
| package | result |
|
||||||
|
| --- | --- |
|
||||||
|
| `omemo2-js` | 404 Not Found |
|
||||||
|
| `@oxjs/omemo` | 404 Not Found |
|
||||||
|
| `omemo-wasm` | 404 Not Found |
|
||||||
|
| `@signalapp/libsignal` | 404 Not Found |
|
||||||
|
| `@signalapp/libsignal-client@0.105.0` | exists (AGPL-3.0-only, native addon) |
|
||||||
|
| `signal-protocol@0.1.3` | exists (GPL-3.0, 2017-era, unmaintained; not a viable maintained OMEMO2 candidate) |
|
||||||
|
|
||||||
|
No maintained browser/WASM-capable OMEMO or OMEMO2 wire-format package is
|
||||||
|
available on the reachable registry. `@signalapp/libsignal-client` provides the
|
||||||
|
session/ratchet engine but does not provide the OMEMO/OMEMO2 XMPP wire mapping
|
||||||
|
(ENC-009/ENC-011 SCE payload/key/MAC stanzas).
|
||||||
|
|
||||||
|
## 4. Honest conclusion (blocker, not success)
|
||||||
|
|
||||||
|
Feasible and verified:
|
||||||
|
- Audited session/ratchet engine + secure session-record persistence (real API,
|
||||||
|
known-answer roundtrip, no homegrown ratchet).
|
||||||
|
|
||||||
|
NOT established and therefore a blocker (per focused-check rule "unsupported
|
||||||
|
format recorded blocker, not success"):
|
||||||
|
- Browser/worker/WASM deployment: distribution is a Node native addon, zero
|
||||||
|
`.wasm` assets; cannot run in the Web client endpoint.
|
||||||
|
- OMEMO/OMEMO2 wire-format layer: no maintained browser-capable OMEMO2 package
|
||||||
|
on the registry; raw `signalDecryptPreKey` re-initiation nuance observed during
|
||||||
|
development was not treated as the acceptance path.
|
||||||
|
|
||||||
|
Task acceptance-1 requires legacy/OMEMO2 AND worker/wasm AND persistence. Two of
|
||||||
|
the three are not established -> escalate a focused remediation that provides or
|
||||||
|
records a verified browser/WASM-capable OMEMO backend and the OMEMO2 wire layer.
|
||||||
|
|
||||||
|
## Reproduction for reviewer
|
||||||
|
- `node experiments/0003-crypto-library/session-probe/smoke.mjs` -> SMOKE_PASS (0)
|
||||||
|
- `node experiments/0003-crypto-library/session-probe/wasm-unsupported.mjs` -> exit 2
|
||||||
|
- `npm view omemo2-js version` -> 404 (proxy-connected registry)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Handoff S00-004 - Browser OMEMO backend spike
|
||||||
|
|
||||||
|
Agent: `auto-crypto-8bf3dcf711b9`
|
||||||
|
Role: crypto
|
||||||
|
Status on handoff: escalated (blocker)
|
||||||
|
|
||||||
|
## Result
|
||||||
|
A focused spike established that an audited Signal session library can provide
|
||||||
|
the session/ratchet engine and session-record persistence **without a homegrown
|
||||||
|
ratchet** (known-answer sealed-sender roundtrip, 631-byte record, reload
|
||||||
|
byte-for-byte). However the same spike established that a browser/worker/WASM
|
||||||
|
deployment and the OMEMO2 wire-format layer are **not available** from any
|
||||||
|
maintained package on the reachable registry:
|
||||||
|
- `@signalapp/libsignal-client@0.105.0` is a Node native addon with zero `.wasm`
|
||||||
|
assets; it cannot run in the Web client endpoint.
|
||||||
|
- No maintained `omemo2-js`/`omemo-wasm`/`@signalapp/libsignal`/OMEMO2 package
|
||||||
|
exists on the registry.
|
||||||
|
|
||||||
|
Per focused-check rule ("unsupported format recorded blocker, not success") and
|
||||||
|
the mandatory escalate protocol, the original task is blocked rather than
|
||||||
|
claimed done.
|
||||||
|
|
||||||
|
## Outputs produced (all inside accepted scope experiments/0003-crypto-library/**)
|
||||||
|
- `experiments/0003-crypto-library/package.json` (exact
|
||||||
|
`@signalapp/libsignal-client@0.105.0`, `--ignore-scripts`).
|
||||||
|
- `experiments/0003-crypto-library/session-probe/smoke.mjs` (known-answer probe,
|
||||||
|
mirrors Signal canonical stores).
|
||||||
|
- `experiments/0003-crypto-library/session-probe/wasm-unsupported.mjs` (unsupported-format probe).
|
||||||
|
- `state/evidence/S00-004-auto-crypto-8bf3dcf711b9.md` (this run's evidence).
|
||||||
|
- ADR `docs/decisions/0003-crypto-library.md` was **not** written because the
|
||||||
|
decision would record an unverified/blocked majority of the acceptance (browser
|
||||||
|
WASM + legacy/OMEMO2) rather than a confirmed capability; the outcome must be
|
||||||
|
that a remediation establishes the browser-capable backend first.
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
- Known-answer probe: PASS (exit 0), stored 631 bytes, byte-for-byte reload.
|
||||||
|
- Unsupported-format probe: exit 2 (no `.wasm`, native addon only).
|
||||||
|
|
||||||
|
## Next input for the remediation
|
||||||
|
Reproduce intent inside `experiments/0003-crypto-library/**`:
|
||||||
|
1. `node session-probe/smoke.mjs` (expect SMOKE_PASS).
|
||||||
|
2. `node session-probe/wasm-unsupported.mjs` (expect exit 2 / blocker line).
|
||||||
|
3. Establish a verified browser/worker/WASM-capable OMEMO2 backend (library with
|
||||||
|
a real `.wasm`/web build and an OMEMO2 wire mapping), or record with traceable
|
||||||
|
upstream primary sources why the browser endpoint must use a maintained
|
||||||
|
encryption layer consistent with ENC-009/ENC-011 client-endpoint encryption.
|
||||||
|
|
||||||
|
# Blocked handoff
|
||||||
|
|
||||||
|
Original: S00-004; reporter: auto-crypto-8bf3dcf711b9; resolution: S00-021.
|
||||||
|
|
||||||
|
Agent timed out; process terminated before escalation
|
||||||
|
|
||||||
|
Next: complete and review the remediation; only a fresh session may continue.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"title": "Establish a verified browser-capable OMEMO2 backend for the Web client endpoint",
|
||||||
|
"reason": "S00-004 cannot establish the browser/worker/WASM and legacy/OMEMO2 wire-format capabilities its acceptance requires. The only maintained, audited session library on the reachable registry (@signalapp/libsignal-client@0.105.0, AGPL-3.0-only) is a Node native addon with zero .wasm assets, so it cannot run in the Web client endpoint; no maintained OMEMO2/omemo-wasm/@signalapp/libsignal package exists on the registry.",
|
||||||
|
"reproduction": "cd experiments/0003-crypto-library && node session-probe/smoke.mjs (SMOKE_PASS, 0) shows session/ratchet+persistence work; node session-probe/wasm-unsupported.mjs (exit 2) shows wasmFiles:0, prebuilds are native *.node only; npm view omemo2-js => 404. Details: state/evidence/S00-004-auto-crypto-8bf3dcf711b9.md",
|
||||||
|
"role": "crypto",
|
||||||
|
"reviewer_role": "security",
|
||||||
|
"scope": [
|
||||||
|
"experiments/0003-crypto-library/**",
|
||||||
|
"docs/decisions/0003-crypto-library.md"
|
||||||
|
],
|
||||||
|
"outcome": "One independently reviewed probe and ADR establish a real browser/worker/WASM-capable OMEMO2 backend (library with a genuine web/WASM build and an OMEMO2 wire mapping, or traceable upstream primary-source proof covering encodings ENC-009/ENC-011), while retaining the verified audited session/ratchet engine and persistence from the existing spike.",
|
||||||
|
"steps": [
|
||||||
|
"Read S00-004 blocker evidence and accepted ENC-003/ENC-009/ENC-011 requirements and ADRs 0000-0002.",
|
||||||
|
"Identify a maintained browser/worker/WASM-capable OMEMO/OMEMO2 encryption library with a real .wasm or web build and an OMEMO2 wire-format mapping; verify its license, primary-source docs, and web build.",
|
||||||
|
"Run a focused known-answer OMEMO2 probe in the browser-compatible target inside experiments/0003-crypto-library/**; record actual results and any unsupported format as a blocker, not success.",
|
||||||
|
"Write/update docs/decisions/0003-crypto-library.md (proposed) recording the real capability and limitations; submit evidence and handoff for independent security review."
|
||||||
|
],
|
||||||
|
"acceptance": [
|
||||||
|
"A browser/worker/WASM-capable OMEMO2 backend executes a known-answer encryption/decryption roundtrip with an authentic web build recorded in evidence.",
|
||||||
|
"The OMEMO2 wire-format mapping is established from real primary sources and does not bypass ENC-009/ENC-011 client-endpoint encryption.",
|
||||||
|
"No homegrown ratchet, stack/spec change without ADR, secrets in evidence, or out-of-scope edits."
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
"Known-answer OMEMO2 roundtrip in the web/WASM target.",
|
||||||
|
"Web/WASM asset presence verified (non-zero real footprint), license and support checked.",
|
||||||
|
"Independent security review confirms no bypass and no unsupported format counted as success."
|
||||||
|
],
|
||||||
|
"depends_on": [],
|
||||||
|
"requires_user_input": false
|
||||||
|
}
|
||||||
+43
-4
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"revision": 55,
|
"revision": 57,
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"S00-001": {
|
"S00-001": {
|
||||||
"status": "done",
|
"status": "done",
|
||||||
@@ -47,8 +47,17 @@
|
|||||||
"resumed_after_blocker": true
|
"resumed_after_blocker": true
|
||||||
},
|
},
|
||||||
"S00-004": {
|
"S00-004": {
|
||||||
"status": "todo",
|
"status": "blocked",
|
||||||
"owner": null
|
"owner": "auto-crypto-8bf3dcf711b9",
|
||||||
|
"started_at": "2026-10-15T08:02:39+00:00",
|
||||||
|
"reason": "Agent timed out; process terminated before escalation",
|
||||||
|
"updated_at": "2026-10-15T08:32:40+00:00",
|
||||||
|
"blocked_by": [
|
||||||
|
"S00-021"
|
||||||
|
],
|
||||||
|
"resume_status": "in_progress",
|
||||||
|
"blocker_report": "state/blockers/S00-021.md",
|
||||||
|
"handoff": "state/handoffs/S00-004.md"
|
||||||
},
|
},
|
||||||
"S00-005": {
|
"S00-005": {
|
||||||
"status": "todo",
|
"status": "todo",
|
||||||
@@ -1268,6 +1277,12 @@
|
|||||||
"reason": null,
|
"reason": null,
|
||||||
"started_at": "2026-10-15T07:27:20+00:00",
|
"started_at": "2026-10-15T07:27:20+00:00",
|
||||||
"updated_at": "2026-10-15T07:27:20+00:00"
|
"updated_at": "2026-10-15T07:27:20+00:00"
|
||||||
|
},
|
||||||
|
"S00-021": {
|
||||||
|
"status": "todo",
|
||||||
|
"owner": null,
|
||||||
|
"external_wait": false,
|
||||||
|
"reason": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"events": [
|
"events": [
|
||||||
@@ -1979,6 +1994,29 @@
|
|||||||
"from": "todo",
|
"from": "todo",
|
||||||
"to": "in_progress",
|
"to": "in_progress",
|
||||||
"reason": null
|
"reason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"revision": 56,
|
||||||
|
"at": "2026-10-15T08:02:39+00:00",
|
||||||
|
"action": "claim",
|
||||||
|
"task": "S00-004",
|
||||||
|
"agent": "auto-crypto-8bf3dcf711b9",
|
||||||
|
"role": "crypto",
|
||||||
|
"from": "todo",
|
||||||
|
"to": "in_progress",
|
||||||
|
"reason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"revision": 57,
|
||||||
|
"at": "2026-10-15T08:32:40+00:00",
|
||||||
|
"action": "escalate",
|
||||||
|
"task": "S00-004",
|
||||||
|
"resolution_task": "S00-021",
|
||||||
|
"agent": "auto-crypto-8bf3dcf711b9",
|
||||||
|
"role": "crypto",
|
||||||
|
"from": "in_progress",
|
||||||
|
"to": "blocked",
|
||||||
|
"reason": "Agent timed out; process terminated before escalation"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"retired_agents": [
|
"retired_agents": [
|
||||||
@@ -1991,6 +2029,7 @@
|
|||||||
"auto-protocol-826aa6bc12ef",
|
"auto-protocol-826aa6bc12ef",
|
||||||
"auto-protocol-902594b44429",
|
"auto-protocol-902594b44429",
|
||||||
"auto-protocol-dcdf92cd067e",
|
"auto-protocol-dcdf92cd067e",
|
||||||
"auto-protocol-59f4d359b339"
|
"auto-protocol-59f4d359b339",
|
||||||
|
"auto-crypto-8bf3dcf711b9"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user