MeshLaw
← Back to security page
SECURITY WHITE PAPER

MeshLaw Security White Paper

2026-05-21 v0.1 · for legal and IT teams to review

This document summarizes MeshLaw’s data processing, isolation, encryption, and access-control policies. It is based on implementation facts (code, migrations, operational settings), and during verification or due diligence we present the relevant lines alongside.

1. System composition

ComponentTechnologyNotes
GatewayMoleculer (Node/Bun) :4098HTTP entry point. JWT verification + RLS context injection
DBPostgreSQL 16 + pgvectorSingle instance. RLS team_id isolation
AI backendvLLM (self-hosted)No external API egress — matter data does not leave for external LLM providers
MailResend (domain verified)Sign-up & password only, no matter content sent
Mobile pushAPNs + FCMtitle/body + data only — no matter body

Hosting: Vultr Seoul region — data resides in Korea. Operational SSH: key auth only (password disabled).

2. Tenant isolation

2.1 PostgreSQL Row-Level Security

Every domain table has FORCE ROW LEVEL SECURITY enabled:

  • Direct isolation (team_id column): matters, clients, advisories, audit_log, notifications, llm_usage, team_token_quota, messages, device_tokens, stripe_webhook_events, invoice_receipts
  • JOIN isolation (matter_id → matters.team_id): matter_events, invoices, timesheet_entries, writs, approvals, matter_refs, vault_files, matter_acl, deadlines

Policy: team_id = app_current_team_id() (USING + WITH CHECK). app_current_team_id() reads the transaction GUC app.team_id.

2.2 NOSUPERUSER role separation

The operational gateway connects to the DB with the meshlaw_app role (NOSUPERUSER · NOBYPASSRLS). Dev / migration tools are separated into a distinct superuser — so no BYPASSRLS user is involved in day-to-day operational traffic. Applied 2026-05-05.

2.3 Transaction GUC injection

sql.begin(async (tx) => 
  await tx`select set_config('app.team_id', ${teamId}, true)`
  await tx`select set_config('app.user_id', ${userId}, true)`
  return fn(tx)

Because it is SET LOCAL, it is released automatically when the transaction ends. 50/50 domain actions apply the wrap. The remaining 7 are intentional raw cases such as the password_hash lookup in auth.signup/login — no data-leak path.

2.4 JWT → ctx.meta.user mapping

The gateway’s onBeforeCall verifies the JWT → injects ctx.meta.user = { id, team, role }. All domain routes are included in AUTH_REQUIRED_PREFIXES, so a missing token yields 401.

2.5 Regression protection

Tenant isolation is regression-tested before every deploy in e2e §7 (RLS):

  • Unauthenticated call → 401
  • Invalid JWT → 401
  • A valid JWT reaches only its own team’s data → 200

3. Personal information protection (PII)

3.1 Resident registration number

Storage: pgp_sym_encrypt encryption (BYTEA rrn_encrypted) + last 4 digits (rrn_last4). Exposure: the client UI forces masking via maskRRN()XXXXXX-XXX{last4}. On a format mismatch it returns an empty string (blocking raw exposure). 8 unit-test regressions.

3.2 Passwords

Stored as bcrypt hashes. The client shows real-time 4-rule visualization on input (8 chars / letters / numbers / special characters). HTTPS only — the iOS Release build does not set NSAllowsArbitraryLoads.

3.3 Token management

  • JWT: short TTL (default 30 minutes), HS256
  • Refresh: rotation + reuse detection. The plaintext is delivered to the user once; the DB stores only the bcrypt hash. On reuse detection the whole chain is revoked

3.4 Per-matter ACL

On top of the RLS team scope, an additional per-matter isolation (matter_acl). 4 roles (owner ⊃ editor ⊃ viewer). Adopted by messenger v0.1.6 — listing a matter’s channels requires viewer or above, writing requires editor or above.

4. Audit log

Actions recorded automatically in the audit_log table:

  • Matter CRUD, ACL grant/revoke
  • Approval approve/reject/request-changes (status_before/after, comment)
  • Invoice issuance, payment receipts (delta_won, total)
  • Usage → invoice/timesheet rollup
  • Reference-document CRUD

Each row: actor_name, target_kind, target_id, http_status, matter_id, meta(JSONB), created_at. RLS enforced — you cannot query another tenant’s audit records.

5. Permission control

5.1 Approvals (matter approvals)

Server-side actor guard — computeApprovalChainAdvance(steps, status, currentUserId). Only the user matching the active step’s approver_id may act. On mismatch, 403 NOT_APPROVER. Same guard on desktop and mobile. 7 unit tests.

5.2 RBAC (system roles)

users.role_idroles table (admin/partner/associate/staff). auth.invite assigns roles (only admins can issue).

6. Data processing

6.1 AI model calls

Self-hosted vLLM (Tailscale internal network) — no external API egress. LLM request bodies (matter context, documents) never leave the organization. Call tokens and cost are recorded in the llm_usage table (RLS team scope).

6.2 Files (Vault)

  • Storage: server disk (storage_directory separated per matter)
  • Metadata: vault_files table (file_name, mime, size_bytes, text_content)
  • Indexing: after text extraction, stored in text_content → ⌘K unified search (RLS enforced)
  • PDF multimodal: pages converted to JPEG with mupdf → vLLM vision encoder (no external egress)

6.3 Mail

Resend domain meshlaw.ai (DKIM/SPF/DMARC verified). Content: sign-up, invite, and password reset only — no matter content.

7. Infrastructure security

7.1 Transport

Gateway: HTTPS only (Caddy). iOS Release / Android Release both use the prod URL only (only Debug uses localhost).

7.2 Mobile push

  • APNs JWT (.p8) — Apple Developer Team ID + Key ID
  • FCM HTTP v1 — Firebase service account JSON
  • Push bodies are metadata only (title/body + data.type routing hint) — no matter body
  • Device tokens: device_tokens table (RLS team scope)

7.3 Operational access

  • SSH access: key-based authentication (recommended for operations)
  • DB: docker-compose binds to 127.0.0.1:54322 as localhost bind only — not exposed to the external internet. Only the gateway container on the host can access it (network_mode: host)
  • Backup: /var/backups/meshlaw-db-*.sql.gz (currently manual, automation planned)
  • Pre-backup is mandatory before applying migrations (operational discipline)

7.4 Stripe payments

Webhook signature verification + idempotency guard (stripe_webhook_events). Card data lives in the Stripe Vault — not stored by us (minimizing PCI scope).

8. Compliance posture

ItemStatus
Personal Information Protection Act (PIPA)Standard processing · DPA provided separately
ISMS-PUnder review (separate notice at time of application)
KISA markUnder review
GDPROut of scope (domestic only)
ZDR (Zero Data Retention)Policy option — contractual addendum on Pro+ plans. Since only self-hosted vLLM is currently used, there is no external training exposure at all

⚠️ ISMS-P / KISA mark are at the review stage. Use of a certification mark is only possible after it is obtained.

9. Incident response

9.1 Token leak

On refresh-reuse detection, the user’s entire chain is automatically revoked. /auth/logout or a password change → revoke all refresh tokens.

9.2 Data exposure

On discovery, immediate docker-compose rollback + DB backup restore.

9.3 User notification

Policy goal: notify of impact scope, recovery steps, and required user actions within 72 hours (PIPA §34 advisory level). There is currently no automation — when it occurs, the operator manually computes the impact scope and sends a bulk email.

10. Data transfer & deletion

  • Export: users can export their own team’s data as JSON (planned for v0.2)
  • Deletion: direct deletion via UI + account deletion by email request → 30-day grace → cascade
  • Retention: indefinite by default. Statutory mandatory retention (e.g., 5 years under income tax law) is the user’s responsibility

11. Verification requests

If you would like fact verification or due-diligence cooperation for this white paper, request it via the contact page or your sales rep’s email — we provide code paths (line numbers) · migration SQL copies · penetration-test results (once performed).

A dedicated security mailbox (e.g., compliance@meshlaw.ai) will be announced separately once domain verification and operational readiness are complete.


If you need a PDF for review

Press ⌘P (Mac) / Ctrl+P (Win) and choose "Save as PDF". Or download the Markdown source.