june ♥
building since june 2026
◈ operatorrecent activity
kepr✦M5 review fixes (v1.56.86): removed the DEAD ops password/session auth stack — CODE + fresh-schema only, NO live migration. (1) cli.zig: dropped the 'Ops panel password' prompt/hash/ops_password_hash write + ops_auth import — kepr setup no longer creates any ops password. (2) main.zig: removed ops_auth import, the loadPersistedSessions boot drain, and refAllDecls(ops_auth). (3) deleted src/web/ops_auth.zig entirely (now unreferenced). (4) removed dead db ops_sessions helpers (putOpsSession/deleteOpsSession/pruneOpsSessions/loadOpsSessions/OpsSession) + their test; dropped the ops_sessions TABLE from schema.sql FRESH def only. (5) ops_credentials untouched (LIVE). Also removed the now-dead App.sessions map (kept sessions_mutex for cli_codes). SHOULD-FIX: (6) server-side length on bio(160)/location(80) in the profile save (UI maxlength is client-only); (7) hoist-and-guard errdefer in getAccountByUsername/getAccountById so a failed dupe can't leak; (8) teamGateDecision simplified to (tier) — dropped the unreachable ops-session arg + the dead operator-resolution branch in teamViewer; (9) reworded stale ops-dashboard/ops-session comments. *** NO EXTENDED_SCHEMA_SQL CHANGE / NO live ALTER-DROP-RENAME *** — verified on a real fresh Postgres: schema applies clean, ops_sessions GONE, ops_credentials KEPT, chat global room seeded; both spec grep gates still ZERO; absence test still asserts; both no-PG gates green; pre-existing team-tier live-PG failure untouched14 hours ago
kepr✦admin phase 11 (v1.56.85): ops fully removed + grep gates pass — deleted the ops_auth import and ALL its usages from server.zig; operator identity is now purely the account tier 'operator' (new isAccountOperator helper; teamViewer/navStateFromRequest/issue handlers/repoHealthJson/repoPage migrated off the never-minted ops-password session). FLAGGED behavior restorations: repoHealthJson (was always-403 via the dead ops gate) and the repo-page operator view now correctly recognise account operators. Reworded every stale /ops-route + ops_ comment across server.zig/pages.zig/db.zig/api.zig/background.zig/retention.zig/main.zig/ops_auth.zig/admin-api.ts; absence test rebuilt to construct its /ops needle from parts so the source is grep-clean while still asserting the route is gone. SPEC GATES PASS: grep '"/ops|/ops/' src/web/{server,pages}.zig and grep ops_ src/web/server.zig build.zig both return zero. CONTINUITY.md updated (ops deleted, /admin live, E2E chat operational, profile heart/bio/location). Kept live infra (ops_credentials/ops_sessions tables, ops_password_hash from kepr setup, ops-api-contracts.md). Both no-PG gates green; pre-existing team-tier live-PG failure untouched14 hours ago
kepr✦admin phase 10 (v1.56.84): profile ♥ + bio/location — added location column (schema.sql + idempotent EXTENDED_SCHEMA_SQL ALTER; bio already existed), location+tier on the db Account struct + both loaders + updateAccountProfile; public profile shows a rose ♥ next to the Sacramento name for team/operator tier and renders bio+location in the identity card, dropping the redundant second @handle; account settings gained a location field (display + save). Access model unchanged (public view, owner-only edit) — no new tier decision. Verified against real Postgres: location+tier round-trip (insert→setTier→update→getAccountByUsername) passes; render test asserts ♥ + location present and profile-identity-handle gone; both no-PG gates green; pre-existing team-tier live-PG failure untouched15 hours ago
kepr✦phase 9 M4 review fixes — key-trust hardening (v1.56.83): 8 items. SECURITY: (1) Chat send now FAILS CLOSED — recomputes fingerprintOf(pubkey) and refuses the whole send if it != the id the server filed it under (no ciphertext leaves on substitution); (2) server STAMPS sender_pubkey_b64 from the session account's registered chat_pubkeys (getChatPubkey), ignoring the client claim — rejects send if no key registered; (3) IndexedDB keys now account-scoped (privkey:/pubkey:<accountId>) + reconciled against the server fingerprint with deliberate rekey on mismatch — no cross-account key reuse in a shared browser; (4) verification UX: full self+peer SHA-256 fingerprints with copy + verify-keys panel. CORRECTNESS: (5) onCleanup registered synchronously at setup (was after onMount awaits → SSE leak); (6,7) errdefer on fp/pk dupes in listChatMembers/chatRoomPubkeys; (8) pubkey registration enforces an uncompressed P-256 point (0x04 + 64 bytes), rejecting arbitrary base64. VERIFIED for real: #2+#8 against live Postgres (sender key server-derived; point validation), #1+#3+#4 in real headless browser (tampered key refused / honest sends ciphertext; account switch reuses no prior key; fingerprint panel renders). Pre-existing team-tier live-PG failure untouched15 hours ago
kepr✦phase 9 chat save 3 — client crypto + Chat island (v1.56.82): lib/crypto.ts E2E construction (Web Crypto, no new deps) — per-device ECDH P-256 keypair, private key NON-extractable in IndexedDB; key agreement ECDH→deriveBits(256)→HKDF-SHA256→AES-GCM-256 (HKDF hardens the raw shared secret beyond the spec's bare-ECDH draft); AES-256-GCM with fresh 96-bit IV per blob; one ciphertext blob per recipient; fingerprint=base64(SHA-256(pubkey)) matching the server. Chat.tsx island: global room + DMs (server-resolved canonical rooms), SSE live stream, history decrypt, encrypt-per-recipient send, self-fingerprint shown for out-of-band verification, no-key members disabled. ACCEPTED TRADEOFFS (flagged for dual crypto review, no answer to my stop-and-ask → recommended defaults): no forward secrecy (static keys; key loss = clean start) and server-mediated key distribution (MITM mitigated only by out-of-band fingerprint compare). VERIFIED in real headless browser against the real crypto.ts: A→B encrypt/decrypt round-trip, ciphertext≠plaintext, sender/non-recipient/tampered all fail, self round-trip, fingerprint stable (8/8); Chat island mounts, registers key, send POST body is ciphertext (plaintext absent). DB-layer privacy boundary verified against real Postgres in save 215 hours ago
kepr✦phase 9 chat save 2 — transport + privacy boundary (v1.56.81): SSE+POST chat delivery (NOT WebSocket — reuses M3's proven cookie-auth/participant SSE pattern; no new upgrade-auth surface). db: insertChatMessage (one opaque ciphertext blob per recipient), listChatHistory (room + recipient_fingerprint + id>after filter), freeChatHistory. Endpoints (operator+team via requireTeamApi): POST /admin/api/chat/send (participant-gated, sender=session, per-recipient blobs, size caps, kind whitelist), GET /admin/api/chat/history (participant-gated + ONLY blobs addressed to the session's own server-known fingerprint), GET /admin/api/chat/stream (SSE; auth+participant+self-key checked BEFORE the stream opens; loop locks DB per-tick never across sleeps; emit-failure returns). PRIVACY: operator cannot read others' DMs — not a member of their room, and even shared-room reads are filtered to its own fingerprint. Verified against real Postgres: A→B blob is readable by B, NOT by A's own view, NOT by outsider C (participant gate false), server stores ciphertext only; the one live-PG failure is the known pre-existing team-tier test15 hours ago
kepr✦phase 9 chat save 1 — schema + key-distribution foundation (v1.56.80): E2E-encrypted team chat tables in schema.sql + mirrored idempotently in db.zig EXTENDED_SCHEMA_SQL (Postgres; account_id is TEXT not the spec's BIGINT; singleton global room seeded idempotently). db layer: setChatPubkey (ON CONFLICT upsert), getChatFingerprint, listChatMembers (team+operator only), ensureDmRoom (canonical sorted dm_key, idempotent, INSERT…RETURNING), isChatRoomParticipant, chatRoomPubkeys. Endpoints (operator+team via requireTeamApi): POST /admin/api/chat/pubkey (server computes fingerprint=base64(SHA-256(pubkey)) — authoritative, not client-trusted — and joins global room), GET members (roster+self_fingerprint+global_room_id), GET dm (canonical room, team-target validated), GET room-pubkeys (participant-gated). All identity from session TeamActor.account_id, never client-supplied. Verified against real Postgres: chat db test (upsert/canonical-DM/participant-gate/room-keys) passes; the one live-PG failure is the known pre-existing team-tier negotiate test15 hours ago
kepr✦admin M3 review fixes (v1.56.79): 6 review items. (1) BLOCKING was inverted — the DB is Postgres via pg_shim, NOT SQLite; SUM(BIGINT) returns numeric and pg_shim.columnI64 only decodes int2/4/8, so the ::bigint cast in adminLoadStorageBars is REQUIRED (without it storage sums read back 0). Kept the cast; added a real-Postgres regression test that runs the exact storage queries through pg_shim and asserts correct bytes WITH the cast and 0 WITHOUT (skips when no PG). Verified live against a throwaway PG; the only PG-suite failure (HTTP team-tier negotiate test) is pre-existing — fails identically on the pre-fix checkpoint, untouched by these fixes. (2) log SSE: emit failure now returns out of the whole handler (was an inner-only break that leaked a polling worker). (3) log tail: poll inclusively (since=last_ts) + de-dupe by counting file-lines consumed at the boundary second, fixing same-second line skips. (4) /admin/api/review now emits the stored agent_status (was hardcoded 'pending', drifting from detail). (5) loadPendingQueue label: alloc-new-then-free-old via temp to close the errdefer double-free window. (6) added missing errdefers on contributor_name + status_owned dupes in the key loaders16 hours ago
repos · 8