Files
lunarfront-app/planning/27_Vault_Password_Manager.md
Ryan Moon 9400828f62 Rename Forte to LunarFront, generalize for any small business
Rebrand from Forte (music-store-specific) to LunarFront (any small business):
- Package namespace @forte/* → @lunarfront/*
- Database forte/forte_test → lunarfront/lunarfront_test
- Docker containers, volumes, connection strings
- UI branding, localStorage keys, test emails
- All documentation and planning docs

Generalize music-specific terminology:
- instrumentDescription → itemDescription
- instrumentCount → itemCount
- instrumentType → itemCategory (on service templates)
- New migration 0027_generalize_terminology for column renames
- Seed data updated with generic examples
- RBAC descriptions updated
2026-03-30 08:51:54 -05:00

10 KiB

Music Store Management Platform

Vault — On-Premise Password Manager

Version 1.0 | Draft

1. Overview

LunarFront includes a built-in password vault for on-premise deployments. Music stores deal with dozens of credentials — supplier portals, distributor accounts, payment processor dashboards, shipping services, insurance logins, ASCAP/BMI licensing portals — and most store owners are not technical enough to adopt a separate password manager.

The vault is a simple, encrypted credential store baked into LunarFront. It encrypts all secrets at rest using AES-256-GCM with a key derived from a master passphrase. The key exists only in server memory and is discarded on restart.

On-premise only. Cloud/hosted deployments would require a more sophisticated key management approach (HSM, envelope encryption) and are out of scope for this design.

2. Security Model

2.1 Encryption

  • Algorithm: AES-256-GCM (authenticated encryption)
  • Key derivation: PBKDF2 or Argon2id from master passphrase + stored salt
  • Each entry encrypted individually (not a single encrypted blob)
  • Each entry gets its own random IV/nonce
  • Encrypted fields: username, password, notes
  • Plaintext fields: entry name, URL, category, tags (searchable)

2.2 Key Lifecycle

  1. First-time setup: Admin sets master passphrase via the admin UI. System generates a random salt, derives the encryption key, encrypts a known verification string, and stores the salt + verification ciphertext in the database.
  2. Server start: Vault is locked. Encryption key is not in memory. All vault API endpoints return 423 Locked.
  3. Unlock: Admin enters master passphrase via the UI. System re-derives the key using the stored salt, verifies it against the verification ciphertext. If correct, key is held in memory. Vault endpoints become functional.
  4. Server restart: Key is lost. Vault returns to locked state.
  5. Lock (manual): Admin can lock the vault without restarting. Key is wiped from memory.

2.3 What's Encrypted vs. Plaintext

Field Storage Reason
name Plaintext Searchable, displayed in list
url Plaintext Searchable, displayed in list
category Plaintext Filterable
username Encrypted Credential
password Encrypted Credential
notes Encrypted May contain secrets
created_at Plaintext Metadata
updated_at Plaintext Metadata
created_by Plaintext Audit

2.4 Development Mode

In development, support a VAULT_PASSPHRASE environment variable to auto-unlock on startup. This avoids blocking the dev workflow. Never use this in production.

3. Database Schema

3.1 vault_config

Stores the salt and verification data. One row per company.

vault_config
  id              uuid PK
  company_id      uuid FK → company
  salt            bytea          -- random salt for key derivation
  verification    bytea          -- encrypted known string for passphrase verification
  kdf_algorithm   varchar(20)    -- 'argon2id' or 'pbkdf2'
  kdf_iterations  integer        -- iteration count / cost parameter
  created_at      timestamptz
  updated_at      timestamptz

3.2 vault_entry

Individual credential entries.

vault_entry
  id              uuid PK
  company_id      uuid FK → company
  name            varchar(255)   -- plaintext, searchable
  url             varchar(500)   -- plaintext, searchable
  category        varchar(100)   -- plaintext, filterable
  username_enc    bytea          -- AES-256-GCM encrypted
  username_iv     bytea          -- nonce for username
  password_enc    bytea          -- AES-256-GCM encrypted
  password_iv     bytea          -- nonce for password
  notes_enc       bytea          -- AES-256-GCM encrypted (nullable)
  notes_iv        bytea          -- nonce for notes (nullable)
  created_by      uuid FK → user
  updated_by      uuid FK → user
  created_at      timestamptz
  updated_at      timestamptz

3.3 vault_access_log

Append-only audit trail for credential access.

vault_access_log
  id              uuid PK
  company_id      uuid FK → company
  entry_id        uuid FK → vault_entry
  user_id         uuid FK → user
  action          varchar(20)    -- 'view', 'copy', 'create', 'update', 'delete'
  field           varchar(20)    -- 'password', 'username', 'notes' (for view/copy)
  ip_address      varchar(45)
  created_at      timestamptz

4. API Endpoints

All vault endpoints are prefixed with /v1/vault.

4.1 Vault Status & Management

Method Path Permission Description
GET /vault/status Any authenticated Returns { locked: bool, initialized: bool }
POST /vault/initialize vault.admin Set master passphrase (first time only)
POST /vault/unlock vault.admin Unlock vault with passphrase
POST /vault/lock vault.admin Lock vault (wipe key from memory)
POST /vault/change-passphrase vault.admin Change master passphrase (re-encrypts all entries)

4.2 Entries

All return 423 Locked when vault is locked.

Method Path Permission Description
GET /vault/entries vault.view List entries (paginated, name/url/category only)
POST /vault/entries vault.edit Create entry
GET /vault/entries/:id vault.view Get entry (decrypts username, password, notes)
PATCH /vault/entries/:id vault.edit Update entry
DELETE /vault/entries/:id vault.edit Delete entry
POST /vault/entries/:id/copy vault.view Log that user copied a credential

4.3 Audit

Method Path Permission Description
GET /vault/audit vault.admin Access log (paginated, filterable by user/entry)

4.4 Locked Response

When vault is locked, all entry/audit endpoints return:

{
  "error": {
    "message": "Vault is locked",
    "statusCode": 423
  }
}

5. Permissions

New permission domain: vault

Slug Description
vault.view View entry list, decrypt and view credentials
vault.edit Create, update, delete entries
vault.admin Initialize, unlock, lock, change passphrase, view audit log

Permission inheritance: vault.adminvault.editvault.view

Default role assignments:

  • Admin: vault.admin
  • Manager: vault.edit
  • All others: No vault access by default

6. Frontend

6.1 Sidebar

New "Vault" item in the Admin section (visible only with vault.view). Shows a lock icon when locked.

6.2 Pages

Vault List Page

  • Search by name/URL
  • Filter by category
  • Table columns: Name, URL, Category, Updated
  • Click row to view details
  • "New Entry" button (requires vault.edit)

Vault Entry Detail

  • Shows name, URL, category
  • Username field with copy button (masked by default, click to reveal)
  • Password field with copy button (masked by default, click to reveal)
  • Notes (hidden by default, click to reveal)
  • Edit/Delete buttons (requires vault.edit)
  • Every reveal/copy action is logged

Vault Locked Page

  • Shown when navigating to vault and it's locked
  • Passphrase input + "Unlock" button (requires vault.admin)
  • If not initialized: "Set Master Passphrase" flow with confirmation input

Vault Setup (First Time)

  • Enter master passphrase (min 16 characters)
  • Confirm passphrase
  • Warning: "This passphrase cannot be recovered. If lost, all vault entries are permanently inaccessible."

6.3 UI Behaviors

  • Password fields use type="password" with a toggle eye icon
  • Copy-to-clipboard auto-clears after 30 seconds
  • Toast notification on copy: "Password copied — clipboard will clear in 30s"
  • Reveal auto-hides after 30 seconds
  • No browser autocomplete on vault forms (autocomplete="off")

7. Operational Considerations

7.1 Passphrase Recovery

There is no recovery mechanism by design. If the master passphrase is lost, all encrypted entries are permanently inaccessible. The vault_config and vault_entry rows can be deleted to start fresh, but the old data is gone.

Document this clearly in the UI and in the setup flow.

7.2 Backup

Database backups include encrypted vault data. Without the master passphrase, the encrypted fields are useless. This is the correct behavior — a stolen backup should not expose credentials.

7.3 Passphrase Change

Changing the master passphrase requires:

  1. Verify old passphrase
  2. Derive new key from new passphrase + new salt
  3. Decrypt every entry with old key, re-encrypt with new key (transaction)
  4. Update vault_config with new salt + verification
  5. Replace in-memory key

This is an expensive operation (scales with entry count) but should be rare.

7.4 Server Restart Behavior

After restart:

  • Vault status: locked
  • All vault API calls: 423
  • Everything else in LunarFront: fully functional
  • Admin unlocks when ready — no urgency, no data loss

7.5 Multiple Servers

If running multiple backend instances behind a load balancer, each instance needs to be unlocked independently (since the key is in-process memory). For multi-instance deployments, consider a shared unlock via Redis (store the derived key in Valkey with a TTL matching the server session). This is an optional enhancement — most on-prem music stores run a single instance.

8. Categories (Defaults)

Pre-populated categories that stores can customize:

  • Suppliers
  • Distributors
  • Payment Processing
  • Shipping & Freight
  • Insurance
  • Licensing (ASCAP, BMI, SESAC)
  • Banking
  • Software & Services
  • Utilities
  • Social Media
  • Website & Hosting
  • Other

9. Implementation Order

  1. Database schema + migration
  2. Encryption service (encrypt, decrypt, key derivation, verification)
  3. Vault status/initialize/unlock/lock endpoints
  4. Entry CRUD endpoints
  5. Audit logging
  6. Add vault.* permissions to RBAC seed
  7. Frontend: locked page + unlock flow
  8. Frontend: entry list + detail + create/edit
  9. Frontend: audit log page
  10. Tests: encryption unit tests, API integration tests