# Deployment/Auth Mode Consolidation Plan

Status: Proposal  
Owner: Server + CLI + UI  
Date: 2026-02-23

## Goal

Keep Paperclip low-friction while making the mode model simpler and safer:

1. `local_trusted` remains the default and easiest path.
2. one authenticated runtime mode supports both private-network local use and public cloud use.
3. onboarding/configure/doctor stay primarily interactive and flagless.
4. Board identity is represented by a real user row in the database, with explicit role/membership integration points.

## Product Constraints (From Review)

1. `onboard` default flow is interactive (no flags required).
2. first mode choice defaults to `local_trusted`, with clear UX copy.
3. authenticated flow gives guidance for private vs public exposure.
4. `doctor` should also be flagless by default (read config and evaluate the selected mode/profile).
5. do not add backward-compatibility alias layers for abandoned mode names.
6. plan must explicitly cover how users/Board are represented in DB and how that affects task assignment and permissions.

## Current Implementation Audit (As Of 2026-02-23)

## Runtime/Auth

- Runtime deployment modes are currently `local_trusted | cloud_hosted` (`packages/shared/src/constants.ts`).
- `local_trusted` actor is currently synthetic:
  - `req.actor = { type: "board", userId: "local-board", source: "local_implicit" }` (`server/src/middleware/auth.ts`).
  - this is not a real auth user row by default.
- `cloud_hosted` uses Better Auth sessions and `authUsers` rows (`server/src/auth/better-auth.ts`, `packages/db/src/schema/auth.ts`).

## Bootstrap/Admin

- `cloud_hosted` requires `BETTER_AUTH_SECRET` and reports bootstrap status from `instance_user_roles` (`server/src/index.ts`, `server/src/routes/health.ts`).
- bootstrap invite acceptance promotes the signed-in user to `instance_admin` (`server/src/routes/access.ts`, `server/src/services/access.ts`).

## Membership/Assignment Integration

- User task assignment requires active `company_memberships` entry for that user (`server/src/services/issues.ts`).
- Local implicit board identity is not automatically a real membership principal; this is a gap for “board as assignable user” semantics.

## Proposed Runtime Model

## Modes

1. `local_trusted`
- no login required
- localhost/loopback only
- optimized for single-operator local setup

2. `authenticated`
- login required for human actions
- same auth stack for both private and public deployments

## Exposure Policy (Within `authenticated`)

1. `private`
- private-network deployments (LAN, VPN, Tailscale)
- low-friction URL handling (`auto` base URL)
- strict host allow policy for private targets

2. `public`
- internet-facing deployments
- explicit public base URL required
- stricter deployment checks in doctor

This is one authenticated mode with two safety policies, not two different auth systems.

## UX Contract

## Onboard (Primary Path: Interactive)

Default command remains:

```sh
pnpm paperclip onboard
```

Interactive server step:

1. ask mode with default selection `local_trusted`
2. copy for options:
- `local_trusted`: "Easiest for local setup (no login, localhost-only)"
- `authenticated`: "Login required; use for private network or public hosting"
3. if `authenticated`, ask exposure:
- `private`: "Private network access (for example Tailscale), lower setup friction"
- `public`: "Internet-facing deployment, stricter security requirements"
4. only if `authenticated + public`, ask for explicit public URL

Flags are optional power-user overrides, not required for normal setup.

## Configure

Default command remains interactive:

```sh
pnpm paperclip configure --section server
```

Same mode/exposure questions and defaults as onboarding.

## Doctor

Default command remains flagless:

```sh
pnpm paperclip doctor
```

Doctor reads configured mode/exposure and applies relevant checks.
Optional flags may exist for override/testing, but are not required for normal operation.

## Board/User Data Model Integration (Required)

## Requirement

Board must be a real DB user principal so user-centric features (task assignment, membership, audit identity) work consistently.

## Target Behavior

1. `local_trusted`
- seed/ensure a deterministic local board user row in `authUsers` during setup/startup.
- actor middleware uses that real user id instead of synthetic-only identity.
- ensure:
  - `instance_user_roles` includes `instance_admin` for this user.
  - company membership can be created/maintained for this user where needed.

2. `authenticated`
- Better Auth sign-up creates user row.
- bootstrap/admin flow promotes that real user to `instance_admin`.
- first company creation flow should ensure creator membership is active.

## Why This Matters

- `assigneeUserId` validation checks company membership.
- without a real board user + membership path, assigning tasks to board user is inconsistent.

## Configuration Contract (Target)

- `server.mode`: `local_trusted | authenticated`
- `server.exposure`: `private | public` (required when mode is `authenticated`)
- `auth.baseUrlMode`: `auto | explicit`
- `auth.publicBaseUrl`: required when `authenticated + public`

No compatibility aliases for discarded naming variants.

## No Backward-Compatibility Layer

This change is a clean cut:

- remove use of old split terminology in code and prompts.
- config schema uses only canonical fields/values above.
- existing dev instances can rerun onboarding or update config once.

## Implementation Phases

## Phase 1: Shared Schema + Config Surface

- `packages/shared/src/constants.ts`: define canonical mode/exposure constants.
- `packages/shared/src/config-schema.ts`: add mode/exposure/auth URL fields.
- `server/src/config.ts` and CLI config types: consume canonical fields only.

## Phase 2: CLI Interactive UX

- `cli/src/prompts/server.ts`: implement defaulted mode prompt and authenticated exposure guidance copy.
- `cli/src/commands/onboard.ts`: keep interactive-first flow; optional overrides only.
- `cli/src/commands/configure.ts`: same behavior for server section.
- `cli/src/commands/doctor.ts`: mode-aware checks from config, flagless default flow.

## Phase 3: Runtime/Auth Policy

- `server/src/index.ts`: enforce mode-specific startup constraints.
- `server/src/auth/better-auth.ts`: implement `auto` vs `explicit` base URL behavior.
- host/origin trust helper for `authenticated + private`.

## Phase 4: Board Principal Integration

- add ensure-board-user startup/setup step:
  - real local board user row
  - instance admin role row
- ensure first-company creation path grants creator membership.
- remove synthetic-only assumptions where they break user assignment/membership semantics.

## Phase 5: UI + Docs

- update UI labels/help text around mode and exposure guidance.
- update docs:
  - `doc/DEPLOYMENT-MODES.md`
  - `doc/DEVELOPING.md`
  - `doc/CLI.md`
  - `doc/SPEC-implementation.md`

## Test Plan

- config schema tests for canonical mode/exposure/auth fields.
- CLI prompt tests for default interactive selections and copy.
- doctor tests by mode/exposure.
- runtime tests:
  - authenticated/private works without explicit URL
  - authenticated/public requires explicit URL
  - private host policy rejects untrusted hosts
- Board principal tests:
  - local_trusted board user exists as real DB user
  - board can be assigned tasks via `assigneeUserId` after membership setup
  - creator membership behavior for authenticated flows

## Acceptance Criteria

1. `pnpm paperclip onboard` is interactive-first and defaults to `local_trusted`.
2. authenticated mode is one runtime mode with `private/public` exposure guidance.
3. `pnpm paperclip doctor` works flagless with mode-aware checks.
4. no extra compatibility aliases for dropped naming variants.
5. Board identity is represented by real DB user/role/membership integration points, enabling consistent task assignment and permission behavior.

## Verification Gate

Before merge:

```sh
pnpm -r typecheck
pnpm test:run
pnpm build
```
