release(v4.0.0): Shade GA — V3.x consolidation + audit prep
Some checks failed
Test / test (push) Has been cancelled
Cross-platform vectors / TypeScript vectors (bun) (push) Has been cancelled
Cross-platform vectors / Kotlin vectors (gradle) (push) Has been cancelled
Docker build and publish / docker (push) Has been cancelled
Publish / publish (push) Has been cancelled

V3.1 → V3.12 consolidated and tagged for the first GA release. Wire
format unchanged from 0.4.x — 4.0 peers interoperate with 0.4.x peers
byte-for-byte. The version bump is semantic: audit-cycle complete,
opt-in surface fully exposed, threat model refreshed for every new
surface.

Highlights:
- All 24 @shade/* packages bumped to 4.0.0 in lockstep.
- CHANGELOG 4.0.0 section is the canonical manifest of what landed.
- THREAT-MODEL extended (§10 fingerprint gates, §11 WebRTC P2P, §12
  Web-Worker boundary) + residual-risks table refreshed.
- OpenAPI now covers all 27 routes: prekey, transfer, KT, inbox,
  bridge, observer, /metrics, /healthz, /ready.
- MIGRATION 0.3.x → 4.0 documented + smoke-tested against
  shade migrate-storage on a real SQLite DB.
- docs/audit/REVIEW-BUNDLE.md + SCOPE.md ready for external reviewer.
- scripts/soak.ts harness for the GA-stable 2-week soak window.
- All V*.md plans archived under docs/archive/ with Status: Done.
- Voice/Video carved out into V5.0; 4.0 audit focuses on the frozen
  non-realtime stack.

Tests: TS 1000/1000 + Kotlin 11/11 cross-platform vectors green.
Docker: gt.zyon.no/stian/shade-prekey:4.0.0 builds and reports
  version 4.0.0 on /health.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 18:35:35 +02:00
parent 8b055912b7
commit e6fdf31b49
298 changed files with 37909 additions and 256 deletions

View File

@@ -88,6 +88,21 @@ export async function ensureClientTables(sql: Sql): Promise<void> {
CREATE INDEX IF NOT EXISTS shade_stream_state_status_idx
ON shade_stream_state(status, direction)
`;
await sql`
CREATE TABLE IF NOT EXISTS shade_peer_verifications (
peer_address TEXT PRIMARY KEY,
fingerprint TEXT NOT NULL,
verified_at BIGINT NOT NULL,
verified_by TEXT NOT NULL,
identity_version BIGINT NOT NULL
)
`;
await sql`
CREATE TABLE IF NOT EXISTS shade_peer_identity_versions (
peer_address TEXT PRIMARY KEY,
version BIGINT NOT NULL
)
`;
}
export async function ensurePrekeyServerTables(sql: Sql): Promise<void> {
@@ -128,3 +143,99 @@ export async function ensurePrekeyServerTables(sql: Sql): Promise<void> {
CREATE INDEX IF NOT EXISTS shade_server_otp_address_idx ON shade_server_one_time_prekeys(address)
`;
}
/**
* Tables for the Key-Transparency log (V3.12).
*
* Append-only invariant for `shade_kt_leaves`:
* - Application code never UPDATEs or DELETEs leaves.
* - A trigger guards against accidental mutation in misconfigured ops:
* even a misbehaving DBA query is rejected, which protects the log
* from silent re-writes.
*/
export async function ensureKTLogTables(sql: Sql): Promise<void> {
await sql`
CREATE TABLE IF NOT EXISTS shade_kt_leaves (
leaf_index BIGINT PRIMARY KEY,
leaf_hash TEXT NOT NULL,
timestamp_ms BIGINT NOT NULL,
operation SMALLINT NOT NULL,
address TEXT NOT NULL,
bundle_hash TEXT NOT NULL
)
`;
await sql`
CREATE INDEX IF NOT EXISTS shade_kt_leaves_address_idx
ON shade_kt_leaves(address)
`;
await sql`
CREATE OR REPLACE FUNCTION shade_kt_block_mutations()
RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'shade_kt_leaves is append-only: % rejected', TG_OP;
END;
$$ LANGUAGE plpgsql
`;
await sql`DROP TRIGGER IF EXISTS shade_kt_leaves_no_update ON shade_kt_leaves`;
await sql`
CREATE TRIGGER shade_kt_leaves_no_update
BEFORE UPDATE OR DELETE OR TRUNCATE ON shade_kt_leaves
FOR EACH STATEMENT
EXECUTE FUNCTION shade_kt_block_mutations()
`;
await sql`
CREATE TABLE IF NOT EXISTS shade_kt_index (
address TEXT PRIMARY KEY,
latest_leaf_index BIGINT NOT NULL,
bundle_hash TEXT NOT NULL,
deleted BOOLEAN NOT NULL DEFAULT FALSE
)
`;
await sql`
CREATE TABLE IF NOT EXISTS shade_kt_sths (
tree_size BIGINT NOT NULL,
timestamp_ms BIGINT NOT NULL,
root_hash TEXT NOT NULL,
index_root TEXT NOT NULL,
log_id TEXT NOT NULL,
signature TEXT NOT NULL,
PRIMARY KEY (tree_size, timestamp_ms, signature)
)
`;
await sql`
CREATE INDEX IF NOT EXISTS shade_kt_sths_timestamp_idx
ON shade_kt_sths(timestamp_ms DESC)
`;
}
export async function ensureInboxServerTables(sql: Sql): Promise<void> {
await sql`
CREATE TABLE IF NOT EXISTS shade_inbox_owners (
address TEXT PRIMARY KEY,
signing_key TEXT NOT NULL
)
`;
await sql`CREATE SEQUENCE IF NOT EXISTS shade_inbox_seq`;
await sql`
CREATE TABLE IF NOT EXISTS shade_inbox_blobs (
address TEXT NOT NULL,
msg_id TEXT NOT NULL,
ciphertext TEXT NOT NULL,
received_at BIGINT NOT NULL,
expires_at BIGINT NOT NULL,
PRIMARY KEY (address, msg_id)
)
`;
await sql`
CREATE INDEX IF NOT EXISTS shade_inbox_addr_expires_idx
ON shade_inbox_blobs(address, expires_at)
`;
await sql`
CREATE INDEX IF NOT EXISTS shade_inbox_addr_received_idx
ON shade_inbox_blobs(address, received_at)
`;
await sql`
CREATE INDEX IF NOT EXISTS shade_inbox_expires_idx
ON shade_inbox_blobs(expires_at)
`;
}