Positioning — jspod is an authentication ladder
Context
While planning Phase 2 of #1 (turn on the IDP / give the welcome page real CTAs), a deeper framing emerged for how jspod should think about auth: not as a binary "on or off," but as a ladder — a sequence of progressively stronger auth states, with jspod's job being to place new users on the lowest viable rung within seconds and make climbing higher visible and easy.
This is a sibling to #3 ("jspod is the single-user personal-pod layer"). Together they form the core product stance.
The ladder
Rung 4 — Hardware key / multi-factor (power users, threat-aware)
↑
Rung 3 — Passkey added (\"you have arrived\")
↑
Rung 2 — User-chosen password (account settings)
↑
Rung 1 — Random password, printed once (jspod's first-run default)
↑
Rung 0 — No auth (--no-auth) (demos / dev only)
Each rung is:
- Safe on its own for the user's current threat model
- A stepping stone — the next rung is always reachable from where you are
- Visible — the UI tells the user where they are and what's one step up
Product principle
Default to the lowest rung that's safe for the current context, and make climbing visible.
Two halves matter equally:
- Lowest safe default: a new user typing npx jspod should not face a password-creation flow, an SSH-key prompt, or a YubiKey setup before they see their pod. They should hit rung 1 (random password seeded automatically, displayed once in the terminal) and be inside their pod within ~60 seconds.
- Visible climb: from the moment they're signed in, the UI should make the next rung obvious — "You're using a random password. Add a passkey to make this pod yours →" — with the action one click away.
This is how mature consumer software ladders: Gmail starts you with a password, nudges you to 2FA, then to a security key, then to passkeys. jspod does the same arc, compressed to a single self-hosted pod's lifetime.
What this changes in jspod
For #1 Phase 2 and beyond:
- Default behaviour: npx jspod → IDP enabled, single-user mode, random password seeded via --single-user-password $(generate), printed once to the terminal banner
- Terminal output: when a random password is seeded, the startup banner shows it clearly:
- "Your initial sign-in password: Abc-XYZ-123"
- "(Change it or switch to a passkey from your pod's account settings)"
- Welcome page: when signed in with a random password, surface a banner / pill: "Rung 1 of 4 — add a passkey →"
- Demo mode: --demo (or similar) opts down to rung 0 (no auth) for talks, screencasts, kiosk use
- --single-user-password <pw> stays available for users who want to set their own initial password (rung 2 at startup)
- --no-auth remains for explicit rung 0 — no implicit fallback
Future / aspirational:
- Rung-2 nudge after first successful sign-in: "Change your password" or "Pick a memorable password"
- Rung-3 nudge prominent in account settings: "Add a passkey"
- Rung-4 nudge in account settings under "Security": "Add a hardware key"
- Recovery story: the climb needs a downstairs too — what happens if someone loses their passkey? Out of scope here, but track in a follow-up.
Why this is the right framing for jspod
- Aligns with the 60-second goal in Friction removal: make first-run zero-friction from discovery to working pod #1: rung 1 is reachable instantly; we don't make users pick an auth method before they've seen the product
- Aligns with Positioning: jspod is the single-user personal-pod layer (not a multi-user server) #3 (single-user positioning): each rung is one person's choice, not a tenancy/admin concern
- Honest about security: rung 1 is not pretending to be strong auth — it's labelled as the first rung, with the next rung visible. Compare to apps that ship default passwords pretending to be production-ready
- Marketing-friendly without lying: "Solid pod in 60 seconds, then make it yours" is a true statement, not a promise of fortress-grade security on rung 1
- Differentiates jspod from JSS: JSS is the substrate that can run at any rung; jspod is the opinionated ladder
Tradeoffs
- More work than just --idp: a random-password generator + clear terminal output + an in-app climb prompt is more than flipping one flag. But it's a strictly better experience than "--idp on" with a single-user mode that immediately 403s on the register endpoint.
- Random-password output couples the terminal and the browser: the user must look at the terminal once. We accept this; it's the only safe way to seed rung 1 without a known-password footgun.
- Adds a "rung" concept users have to learn: minor; the word "rung" doesn't have to appear in the UI. The mental model is just "current auth → next step." Plain English everywhere.
Non-goals
- Inventing auth methods JSS doesn't support
- Building a recovery/downgrade flow now (separate ticket)
- Forcing climbs (every rung is a stable destination if the user wants to stop there)
- Hiding the ladder from users who want full control (every rung is also reachable via CLI flag for advanced users)
Acceptance
When this issue is resolved:
- A new user running npx jspod is signed in within ~60 seconds without picking an auth method
- The terminal makes the initial credential obvious and ephemeral-feeling
- The signed-in UI shows the next rung as a clear, single-click action
- The README's auth section reads as a ladder, not a list of options
- --no-auth, --single-user-password <pw>, and the future --demo are all positioned as choose-your-starting-rung overrides
References
Positioning — jspod is an authentication ladder
Context
While planning Phase 2 of #1 (turn on the IDP / give the welcome page real CTAs), a deeper framing emerged for how jspod should think about auth: not as a binary "on or off," but as a ladder — a sequence of progressively stronger auth states, with jspod's job being to place new users on the lowest viable rung within seconds and make climbing higher visible and easy.
This is a sibling to #3 ("jspod is the single-user personal-pod layer"). Together they form the core product stance.
The ladder
Rung 4 — Hardware key / multi-factor (power users, threat-aware) ↑ Rung 3 — Passkey added (\"you have arrived\") ↑ Rung 2 — User-chosen password (account settings) ↑ Rung 1 — Random password, printed once (jspod's first-run default) ↑ Rung 0 — No auth (--no-auth) (demos / dev only)Each rung is:
Product principle
Default to the lowest rung that's safe for the current context, and make climbing visible.
Two halves matter equally:
This is how mature consumer software ladders: Gmail starts you with a password, nudges you to 2FA, then to a security key, then to passkeys. jspod does the same arc, compressed to a single self-hosted pod's lifetime.
What this changes in jspod
For #1 Phase 2 and beyond:
Future / aspirational:
Why this is the right framing for jspod
Tradeoffs
Non-goals
Acceptance
When this issue is resolved:
References