Skip to content

Commit ec27890

Browse files
feat: enable IDP, seed me/me, localhost-only by default (#1 phase 2)
The welcome page is HEAD-adaptive: it reveals "Sign in" or "Sign up" based on what the IDP advertises. With the IDP off (jspod's previous default), neither button rendered and the only visible CTA was a docs link going off-site. Turning the IDP on with single-user seeding flips that page from "read the docs" to "sign in here." - Flip default `--host` to 127.0.0.1 (was 0.0.0.0). Personal pods almost always want localhost; making this the default also lets us safely seed deliberately-weak rung-1 credentials. - Replace `--no-multiuser`-only single-user behaviour with full single-user mode: `--no-multiuser --single-user --idp --single-user-password me`. JSS hardcodes the username on root pods to 'me' (server.js:970), so credentials end up `me` / `me` — symmetric, memorable, clearly a placeholder. - Banner adds a "Sign In (rung 1 of the auth ladder)" section showing username, password, and the climb hint. References the ladder framing from issue #6. - Loud red warning if user passes `--host 0.0.0.0` (or any non-loopback host) telling them the well-known credentials are now reachable beyond localhost. - README: First Run Guide rewritten around the ladder. Rungs 0-4 table, climb instructions, and JSS_SINGLE_USER_PASSWORD escape hatch for users who want a custom rung-1 password. - Multi-user mode (`--multiuser`) still works: passes `--idp` so the IDP is available for registration, but no `--single-user-*` flags so registration stays open. - Help text: --host default updated to 127.0.0.1. - Bump jspod to 0.0.10. This addresses issue #1 phase 2, executed through the lens of issue #3 (single-user positioning) and issue #6 (auth ladder). Refs #1 #3 #6
1 parent daec447 commit ec27890

3 files changed

Lines changed: 75 additions & 7 deletions

File tree

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jspod --multiuser
7979
```
8080
Options:
8181
-p, --port <number> Port to listen on (default: 5444)
82-
-h, --host <address> Host to bind to (default: 0.0.0.0)
82+
-h, --host <address> Host to bind to (default: 127.0.0.1)
8383
-r, --root <path> Data directory (default: ./pod-data)
8484
--multiuser Enable multi-user mode
8585
--no-auth Disable authentication
@@ -198,7 +198,7 @@ Under the hood, jspod runs JavaScriptSolidServer with these options:
198198
```javascript
199199
{
200200
port: 5444, // Memorable, low collision with common dev servers
201-
host: '0.0.0.0', // Accept connections from anywhere
201+
host: '127.0.0.1', // Localhost-only by default (rung-1 credentials)
202202
root: './pod-data', // Local data directory
203203
multiuser: false, // Single pod per server
204204
TOKEN_SECRET: (auto) // JWT secret (auto-generated, change for production)
@@ -216,6 +216,35 @@ npx jspod
216216

217217
> Running over SSH, in CI, or in a non-interactive terminal? jspod skips auto-open and prints the URL instead. You can also pass `--no-open` to disable it explicitly.
218218
219+
**Step 3**: Sign in (rung 1 of the auth ladder)
220+
221+
The first time you start jspod, an IDP account is seeded with deliberately weak default credentials:
222+
223+
| Field | Value |
224+
| -------- | ----- |
225+
| Username | `me` |
226+
| Password | `me` |
227+
228+
Click **Sign in** on the welcome page, then point a Solid app (like [Pilot](https://solid-apps.github.io/pilot/)) at `http://localhost:5444` and sign in with `me` / `me`.
229+
230+
> **Why are the defaults so weak?** jspod ships you onto the first rung of the auth ladder in under a minute, then guides you up. Rung 1 is **only safe on localhost** — jspod binds to `127.0.0.1` by default for exactly this reason. Once you're in, change the password (rung 2) or add a passkey (rung 3) from your pod's account settings. See [issue #6](https://github.com/JavaScriptSolidServer/jspod/issues/6) for the ladder rationale.
231+
232+
**Step 4**: Climb the ladder
233+
234+
| Rung | Auth | How |
235+
| ---- | --------------------- | -------------------------------------------- |
236+
| 0 | None | `npx jspod --no-auth` (demos / dev only) |
237+
| 1 | `me` / `me` | **Default.** Localhost-only. |
238+
| 2 | Your password | Change it from your pod's account settings |
239+
| 3 | Passkey | Add a passkey from account settings |
240+
| 4 | Hardware key / MFA | Power-user setup |
241+
242+
Override the default password without going through the UI:
243+
244+
```bash
245+
JSS_SINGLE_USER_PASSWORD='your-password' npx jspod
246+
```
247+
219248
**Step 3**: Register with a passkey
220249
- Click "Register" or "Sign Up"
221250
- Use your device's biometric auth (fingerprint, Face ID, etc.)

index.js

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,20 @@ function formatUrl(host, port) {
3131
const args = process.argv.slice(2);
3232
const options = {
3333
port: 5444,
34-
host: '0.0.0.0',
34+
host: '127.0.0.1',
3535
root: './pod-data',
3636
multiuser: false,
3737
auth: true,
3838
open: true
3939
};
4040

41+
// Auth-ladder rung-1 credentials. See issue #6: jspod ships a deliberately
42+
// weak default sign-in so the new user is on a working pod within seconds,
43+
// with a clearly-marked path to climb (change password / add a passkey).
44+
// Safe because the default host is localhost-only (127.0.0.1).
45+
const RUNG_1_USERNAME = 'me';
46+
const RUNG_1_PASSWORD = 'me';
47+
4148
for (let i = 0; i < args.length; i++) {
4249
const arg = args[i];
4350

@@ -66,7 +73,7 @@ for (let i = 0; i < args.length; i++) {
6673
console.log(chalk.yellow(' jspod') + chalk.dim(' [options]\n'));
6774
console.log(chalk.white('Options:'));
6875
console.log(chalk.green(' -p, --port ') + chalk.yellow('<number>') + chalk.dim(' Port to listen on (default: 5444)'));
69-
console.log(chalk.green(' -h, --host ') + chalk.yellow('<address>') + chalk.dim(' Host to bind to (default: 0.0.0.0)'));
76+
console.log(chalk.green(' -h, --host ') + chalk.yellow('<address>') + chalk.dim(' Host to bind to (default: 127.0.0.1)'));
7077
console.log(chalk.green(' -r, --root ') + chalk.yellow('<path>') + chalk.dim(' Data directory (default: ./pod-data)'));
7178
console.log(chalk.green(' --multiuser') + chalk.dim(' Enable multi-user mode'));
7279
console.log(chalk.green(' --no-auth') + chalk.dim(' Disable authentication'));
@@ -125,6 +132,28 @@ console.log(chalk.cyan(' ├─ ') + chalk.white('Host: ') + chalk.yellow
125132
console.log(chalk.cyan(' ├─ ') + chalk.white('Pod Root: ') + chalk.yellow(options.root));
126133
console.log(chalk.cyan(' └─ ') + chalk.white('Mode: ') + (options.multiuser ? chalk.yellow('Multi-user') : chalk.yellow('Single-user')));
127134

135+
if (options.auth && !options.multiuser) {
136+
console.log('\n' + chalk.bold.white('🔑 Sign In (rung 1 of the auth ladder):\n'));
137+
console.log(chalk.cyan(' ├─ ') + chalk.white('Username: ') + chalk.bold.green(RUNG_1_USERNAME));
138+
console.log(chalk.cyan(' ├─ ') + chalk.white('Password: ') + chalk.bold.green(RUNG_1_PASSWORD));
139+
console.log(chalk.cyan(' └─ ') + chalk.dim('Climb: change the password or add a passkey from account settings'));
140+
141+
// Loud warning if the rung-1 known credentials are reachable beyond
142+
// the local machine. See issue #6 ("auth ladder"): rung 1 is only
143+
// safe when the host is loopback-only. Any other bind exposes the
144+
// well-known me/me credentials to the LAN (or worse).
145+
const isLoopback =
146+
options.host === '127.0.0.1' ||
147+
options.host === 'localhost' ||
148+
options.host === '::1';
149+
if (!isLoopback) {
150+
console.log('\n' + chalk.bold.red('⚠ Warning: ') + chalk.yellow(
151+
`--host ${options.host} exposes the well-known me/me credentials beyond localhost.`
152+
));
153+
console.log(chalk.dim(' Set --single-user-password via env (JSS_SINGLE_USER_PASSWORD) or run on 127.0.0.1.'));
154+
}
155+
}
156+
128157
console.log('\n' + chalk.bold.white('✨ Features:\n'));
129158
console.log(chalk.cyan(' ├─ ') + chalk.green('Solid Protocol ') + chalk.bold.green('✓'));
130159
console.log(chalk.cyan(' ├─ ') + chalk.green('WebID Auth ') + (options.auth ? chalk.bold.green('✓') : chalk.dim('✗')));
@@ -150,8 +179,18 @@ const jssArgs = [
150179
'--conneg'
151180
];
152181

153-
if (!options.multiuser) {
154-
jssArgs.push('--no-multiuser');
182+
if (options.multiuser) {
183+
// Multi-user mode is an explicit opt-out from jspod's single-user
184+
// positioning (#3). The IDP stays available so users can register.
185+
if (options.auth) jssArgs.push('--idp');
186+
} else {
187+
// Default: single-user personal pod with rung-1 credentials seeded.
188+
// The pod, IDP, and known credentials are created on first start;
189+
// every subsequent start is a no-op (JSS is idempotent on the seed).
190+
jssArgs.push('--no-multiuser', '--single-user');
191+
if (options.auth) {
192+
jssArgs.push('--idp', '--single-user-password', RUNG_1_PASSWORD);
193+
}
155194
}
156195

157196
// Start JSS with enhanced PATH to find the binary

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jspod",
3-
"version": "0.0.9",
3+
"version": "0.0.10",
44
"description": "JavaScript Solid Pod - Just works, batteries included",
55
"type": "module",
66
"main": "index.js",

0 commit comments

Comments
 (0)