logicsrc/apps/pwa
Anthony Ettinger 5bebc50beb
pwa: read vault values in the browser with a pasted identity key (#225)
* pwa: read vault values in the browser with a pasted identity key

The web app listed vaults but could never show a value: decryption needs the
member's X25519 secret key, which only lives in identity.json on the machine
that ran `logicsrc login`.

Each vault now has a page (/teams/:slug/vaults/:id, linked from the
dashboard) listing its secret names. Paste the identity key (or the whole
identity.json) and public/vault.js decrypts in the browser with the same
libsodium calls as the CLI: crypto_box_seal_open for the grant, then
crypto_secretbox_open_easy per value. Nothing is sent to the server.

- The pasted key is checked against the public key the server has on file
  before use, so a key from another machine is named, not a generic failure.
- Show / Copy per value, Download .env, Forget key. The key is kept in
  sessionStorage unless "remember on this device" is ticked.
- A member with no identity yet can create one in the browser. That
  registers only the public half, and is offered only when no key is
  registered, since replacing one would orphan every grant sealed to it.
- libsodium is served from node_modules at /vendor (no CDN), like simplewebauthn.
- Vault pages are no-store and the service worker no longer caches no-store pages.
- CLI: `logicsrc teams key` prints the secret key to stdout alone (pipe to
  pbcopy) and warns when the server holds a different public key. The page
  also gives a jq one-liner for CLIs released before this command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pwa: rename data-secret to data-key-name

ThreatCrush read data-secret="…" as a hardcoded credential (2 high, both
the attribute name, never a value).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CLI 0.5.0 for teams key; install via install.sh, not npm

@logicsrc/cli is not on npm; the CLI ships through
curl -fsSL https://logicsrc.com/install.sh | sh, which builds master and
reports the version from packages/cli/package.json. 0.5.0 marks the first
build with teams key, and the vault page now says so and points older
installs at the jq fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:55:34 -07:00
..
public pwa: read vault values in the browser with a pasted identity key (#225) 2026-10-04 02:55:34 -07:00
src pwa: read vault values in the browser with a pasted identity key (#225) 2026-10-04 02:55:34 -07:00
test pwa: read vault values in the browser with a pasted identity key (#225) 2026-10-04 02:55:34 -07:00
.env.example feat(pwa): logicsrc credentials app — real auth + Turso, redesigned; retire commandboard-api credshare 2026-07-13 14:29:29 +00:00
.gitignore feat(pwa): logicsrc credentials app — real auth + Turso, redesigned; retire commandboard-api credshare 2026-07-13 14:29:29 +00:00
package.json pwa: read vault values in the browser with a pasted identity key (#225) 2026-10-04 02:55:34 -07:00
Procfile feat(pwa): logicsrc credentials app — real auth + Turso, redesigned; retire commandboard-api credshare 2026-07-13 14:29:29 +00:00
railway.json feat(pwa): logicsrc credentials app — real auth + Turso, redesigned; retire commandboard-api credshare 2026-07-13 14:29:29 +00:00
README.md feat(pwa): move app.logicsrc.com from Turso/libSQL to Postgres via @profullstack/libsql-pg (#218) 2026-09-25 09:28:40 -07:00

@logicsrc/pwa — LogicSRC credentials

Express app on Postgres for team credential sharing: auth (email/password, passkeys, CoinPay OAuth, sessions, lsk_ CLI API keys) + end-to-end-encrypted team vaults. Zero-knowledge — the server only stores ciphertext, per-member sealed vault keys, and identity public keys. Decryption happens in the logicsrc CLI.

cp .env.example .env      # set SESSION_SECRET; DATABASE_URL=postgres://… for prod (else a local libSQL file db)
npm install
npm start                 # migrates on boot, serves on :8080

Database

Production runs on Postgres: DATABASE_URL must be a postgres:// URL and the app refuses to start on anything else there (a leftover libsql:// value is called out by name). The client is @profullstack/libsql-pg, which keeps the @libsql/client surface the code was written against and rewrites the remaining SQLite idioms per statement. Development and the tests use libSQL itself (file:./data/local.db by default, :memory: in tests).

Migrations run at boot and live in two dialect copies with the same file names: src/migrations/ (SQLite) and src/migrations-pg/ (Postgres, generated with npx libsql-pg convert-schema and reviewed). npm run migrate applies the copy matching DATABASE_URL. To move an existing Turso database:

DATABASE_URL=postgres://… npm run migrate                                   # schema
npx libsql-pg copy --from "$TURSO_DATABASE_URL" --token "$TURSO_AUTH_TOKEN" \
  --to "$DATABASE_URL" --verify                                             # rows

Set PWA_TEST_DATABASE_URL=postgres://… to run the test suite against a real Postgres (it drops and recreates the public schema of that database).

The CLI connects with LOGICSRC_API=<origin> logicsrc login (browser OAuth-PKCE loopback → an lsk_ key). See docs/credential-sharing.md in the repo root.