Skip to content

Commit 6950230

Browse files
committed
flightcheck script, lifecycle docs, README/CLAUDE updates
1 parent 8a38f4a commit 6950230

4 files changed

Lines changed: 121 additions & 23 deletions

File tree

.claude/CLAUDE.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,24 @@ npm run test:static:notification
5959
# Single spec file
6060
npm run pretest && NODE_EXTRA_CA_CERTS=ssl.crt npx playwright test tests/static/autofill-forms.spec.ts
6161

62-
# Public (live site) tests
62+
# Public (live site) tests — NEVER run these; they hit real external sites
6363
npm run test:public:debug
6464

6565
# Accessibility tests
6666
npm run test:a11y:browser
6767
npm run test:a11y:web
6868

6969
# Utilities
70-
npm run status # check environment prerequisites
70+
npm run flightcheck # check environment prerequisites
7171
npm run prettier:fix # format all files
7272
npm run typecheck # typecheck scripts/ and tests/
73+
npm run setup:install # generate and write BW_INSTALLATION_ID / BW_INSTALLATION_KEY
74+
npm run setup:crypto # generate and write crypto values to .env
75+
npm run setup:crypto:reset # regenerate crypto values after VAULT_EMAIL/PASSWORD change
76+
npm run setup:flags # sync feature flags from REMOTE_VAULT_CONFIG_MATCH into flags.json
7377
npm run setup:vault # create account + seed vault
7478
npm run seed:vault:ciphers # seed vault only (account must exist)
75-
npm run setup:crypto:reset # regenerate crypto values after VAULT_EMAIL/PASSWORD change
79+
npm run seed:vault:import # import a vault JSON file (requires VAULT_IMPORT_FILE)
7680
```
7781

7882
## Debug Helpers
@@ -84,7 +88,7 @@ npm run setup:crypto:reset # regenerate crypto values after VAULT_EMAIL/PASSW
8488

8589
1. **No real credentials in test data**: All vault passwords are fake (e.g., `"fakeBasicFormPassword"`). Prefix test credential values with `fake`.
8690
2. **No secrets in source**: Crypto material, master password hashes, and API keys live only in `.env` (gitignored). Generated by `npm run setup:crypto` — never set manually.
87-
- `VAULT_EMAIL` is used as a PBKDF2 salt — changing it invalidates `MASTER_PASSWORD_HASH` and requires `npm run setup:crypto:reset` + a fresh DB.
91+
- `VAULT_EMAIL` is used as a PBKDF2 salt and as a seeding value for generating the install keys — changing it invalidates `MASTER_PASSWORD_HASH` and requires `npm run setup:crypto:reset` + a fresh DB.
8892
3. **Zero-knowledge invariant**: Account creation in `create-account.ts` sends a pre-hashed master password and encrypted key material — never the plaintext password — to the server API.
8993
4. **Downloads disabled**: The browser fixture sets `acceptDownloads: false`.
9094

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ As a secondary concern, BIT aspires to track and anticipate feature compatibilit
7575

7676
## Returning Workflow
7777

78-
Run `npm run status` to check which prerequisites are satisfied and get specific commands for anything that needs attention. Typically all that's needed is:
78+
Run `npm run flightcheck` to check which prerequisites are satisfied and get specific commands for anything that needs attention. Typically you only need to do the initial set up once; after that all that's needed is:
7979

8080
```bash
8181
docker compose up -d --wait # start the vault server
82-
npm run start:test-site
8382
npm run test:static:debug
8483
```
8584

@@ -89,7 +88,7 @@ npm run test:static:debug
8988

9089
> Important! Once you've generated installation and crypto values for your `.env` file, DO NOT CHANGE the seeding values (`VAULT_EMAIL`, `VAULT_PASSWORD`, `KDF_ITERATIONS`). Doing so requires regenerating your installation and crypto secret values and rebuilding/updating server.
9190
92-
> If you do need to change `VAULT_EMAIL` or `VAULT_PASSWORD`, run `npm run setup:crypto:reset` to clear and regenerate the derived crypto values, then `docker compose down -v && docker compose up -d --wait` for a fresh database, then `npm run setup:vault`.
91+
> If you do need to change `VAULT_EMAIL` or `VAULT_PASSWORD`, run `npm run setup:crypto:reset` to clear and regenerate the derived crypto values, then `docker compose down -v && docker compose up -d --wait` for a fresh database, then `npm run setup:vault`. Note that changing `VAULT_EMAIL` may also require regenerating install keys (`npm run setup:install`) and rebuilding the vault, depending on what you're trying to do.
9392
9493
- Run `npm run setup:install` to generate and add installation values to your dotfile
9594
- Alternatively, you can generate them at `https://bitwarden.com/host` and add them to your dotfile manually as `BW_INSTALLATION_ID` and `BW_INSTALLATION_KEY`
@@ -125,11 +124,13 @@ Using Docker Compose will set up all the services required by the extension for
125124

126125
Create and start the containers and volumes with `docker compose up -d --build --remove-orphans`, and teardown with `docker compose down -v`
127126

128-
> If the image pull fails with a network error (e.g. `unexpected EOF`), re-run `docker compose pull` and then retry. If it continues to fail, try increasing the timeout: `COMPOSE_HTTP_TIMEOUT=120 docker compose pull`.
127+
> If the image pull fails with a network error (e.g. `unexpected EOF`), re-run `docker compose pull` and then retry. If it continues to fail, try increasing the timeout: `COMPOSE_HTTP_TIMEOUT=120 docker compose pull`. A common underlying cause is endpoint protection or firewall rules blocking the pull.
129128
130-
> If startup fails with `port is already allocated`, a previous container session is holding the port — often a prior BIT stack (e.g. after updating the image version). Run `npm run status` to identify which project owns the port and get the exact teardown command.
129+
> If startup fails with `port is already allocated`, a previous container session is holding the port — often a prior BIT stack (e.g. after updating the image version). Run `npm run flightcheck` to identify which project owns the port and get the exact teardown command.
131130
132-
> Each compose project uses its own database volume. Switching to a different vault image version means a fresh database — re-run `npm run setup:vault` after bringing the new stack up. Old project volumes are not removed automatically; `npm run status` will list any orphaned BIT volumes and show the `docker volume rm` commands to clean them up.
131+
> Each compose project uses its own database volume. Switching to a different vault image version means a fresh database — re-run `npm run setup:vault` after bringing the new stack up. Old project volumes are not removed automatically. If `npm run flightcheck` detects a wrong-version or missing stack, it will list any BIT volumes from other projects and show the `docker volume rm` commands to clean them up.
132+
133+
> If you have previously seeded a vault, you don't need to run setup again — just start it. This is especially relevant when switching between several environments (for example, different server images or feature flag configurations): each has its own volume, so bringing one back up restores its prior state as-is.
133134
134135
### Seeding Your Vault
135136

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"seed:vault:ciphers": "./scripts/seeder.sh",
4444
"seed:vault:ciphers:refresh": "REFRESH=true ./scripts/seeder.sh",
4545
"seed:vault:import": "./scripts/vault-import.sh",
46-
"status": "./scripts/status.sh",
46+
"flightcheck": "./scripts/flightcheck.sh",
4747
"setup:all": "./scripts/first-time-setup.sh",
4848
"setup:extension": "rimraf clients && git clone https://github.com/bitwarden/clients.git clients && cd clients && npm ci",
4949
"setup:install": "ts-node ./scripts/generate-installation.ts",
Lines changed: 105 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@ else
3838
set -o allexport; source "$ROOT_DIR/.env"; set +o allexport
3939

4040
missing=()
41-
for var in VAULT_EMAIL VAULT_PASSWORD PAGES_HOST VAULT_HOST_URL VAULT_HOST_PORT; do
41+
for var in \
42+
VAULT_EMAIL VAULT_PASSWORD \
43+
PAGES_HOST VAULT_HOST_URL VAULT_HOST_PORT \
44+
CLI_SERVE_HOST CLI_SERVE_PORT \
45+
EXTENSION_BUILD_PATH \
46+
BW_DOMAIN \
47+
BW_DB_PROVIDER BW_DB_SERVER BW_DB_DATABASE \
48+
BW_DB_USERNAME BW_DB_PASSWORD BW_DB_PORT \
49+
BW_ENABLE_SSL BW_SSL_CERT BW_SSL_KEY; do
4250
val=$(eval "echo \"\${${var}:-}\"")
4351
if [ -z "$val" ] || [[ "$val" == *"<"* ]]; then
4452
missing+=("$var")
@@ -53,6 +61,19 @@ else
5361
fi
5462
fi
5563

64+
# ── Vault import file (optional) ──────────────────────────────────────────────
65+
if [ -n "${VAULT_IMPORT_FILE:-}" ]; then
66+
if [ -f "$ROOT_DIR/$VAULT_IMPORT_FILE" ]; then
67+
row "$OK" "Vault import file" "$VAULT_IMPORT_FILE"
68+
else
69+
row "$ERR" "Vault import file" "not found ($VAULT_IMPORT_FILE)"
70+
errors=$((errors + 1))
71+
fi
72+
else
73+
row "$WARN" "Vault import file" "not set (optional)"
74+
warnings=$((warnings + 1))
75+
fi
76+
5677
# ── SSL certificates ──────────────────────────────────────────────────────────
5778
if [ -f "$ROOT_DIR/ssl.crt" ] && [ -f "$ROOT_DIR/ssl.key" ]; then
5879
row "$OK" "SSL certificates" "ssl.crt / ssl.key"
@@ -119,20 +140,34 @@ else
119140
if [ -n "$BW_ID" ]; then
120141
BW_HEALTH=$(docker inspect --format "{{.State.Health.Status}}" "$BW_ID" 2>/dev/null || true)
121142
ACTUAL_IMAGE=$(docker inspect --format "{{.Config.Image}}" "$BW_ID" 2>/dev/null || true)
143+
BW_PROJECT=$(docker inspect --format \
144+
"{{index .Config.Labels \"com.docker.compose.project\"}}" "$BW_ID" 2>/dev/null || true)
145+
BW_NAME=$(docker inspect --format "{{.Name}}" "$BW_ID" 2>/dev/null | sed 's|^/||' || true)
146+
BW_PORTS=$(docker ps --filter "id=$BW_ID" --format "{{.Ports}}" 2>/dev/null || true)
147+
DB_IMAGE=$(docker ps \
148+
--filter "label=com.docker.compose.project=${BW_PROJECT}" \
149+
--filter "label=com.docker.compose.service=db" \
150+
--format "{{.Image}}" 2>/dev/null | head -1 || true)
151+
152+
show_container_details() {
153+
hint "name: ${BW_NAME}${DB_IMAGE:+ | db: ${DB_IMAGE}}${BW_PORTS:+ | ports: ${BW_PORTS}}"
154+
}
122155

123156
if [ "$BW_HEALTH" = "unhealthy" ]; then
124157
row "$ERR" "Docker" "bitwarden unhealthy (${ACTUAL_IMAGE})"
158+
show_container_details
125159
hint "docker compose logs bitwarden"
126160
errors=$((errors + 1))
127161
elif [ "$BW_HEALTH" = "starting" ]; then
128162
row "$WARN" "Docker" "bitwarden still starting up... (${ACTUAL_IMAGE})"
163+
show_container_details
129164
warnings=$((warnings + 1))
130165
elif [ "$ACTUAL_IMAGE" = "$EXPECTED_IMAGE" ]; then
131166
row "$OK" "Docker" "running (${ACTUAL_IMAGE})"
167+
show_container_details
132168
else
133-
BW_PROJECT=$(docker inspect --format \
134-
"{{index .Config.Labels \"com.docker.compose.project\"}}" "$BW_ID" 2>/dev/null || true)
135169
row "$WARN" "Docker" "running wrong version (got: ${ACTUAL_IMAGE}, want: ${EXPECTED_IMAGE})"
170+
show_container_details
136171
if [ -n "$BW_PROJECT" ]; then
137172
hint "docker compose -p ${BW_PROJECT} down && docker compose up -d --build --wait"
138173
else
@@ -208,24 +243,82 @@ if curl -sf $CACERT_OPT --max-time 3 "${VAULT_URL}/alive" > /dev/null 2>&1; then
208243
hint "npm run setup:vault"
209244
errors=$((errors + 1))
210245
fi
246+
247+
# Check feature flags
248+
if [ -n "${REMOTE_VAULT_CONFIG_MATCH:-}" ]; then
249+
row "$OK" "Feature flags" "synced from remote (${REMOTE_VAULT_CONFIG_MATCH})"
250+
else
251+
CONFIG_JSON=$(curl -s $CACERT_OPT --max-time 5 "${VAULT_URL}/api/config" 2>/dev/null || true)
252+
FLAG_COUNT=$(python3 -c "
253+
import json, sys
254+
try:
255+
d = json.loads(sys.argv[1])
256+
print(len(d.get('featureStates', {})))
257+
except:
258+
print(-1)
259+
" "$CONFIG_JSON" 2>/dev/null || echo -1)
260+
261+
if [ "$FLAG_COUNT" -gt 0 ] 2>/dev/null; then
262+
row "$OK" "Feature flags" "${FLAG_COUNT} flags loaded"
263+
elif [ "$FLAG_COUNT" -eq 0 ] 2>/dev/null; then
264+
row "$WARN" "Feature flags" "featureStates is empty — flags may not be configured"
265+
hint "npm run setup:flags # sync from a remote config source"
266+
warnings=$((warnings + 1))
267+
else
268+
row "$WARN" "Feature flags" "could not read /api/config"
269+
warnings=$((warnings + 1))
270+
fi
271+
fi
211272
else
212273
row "$WARN" "Vault" "not reachable (${VAULT_URL})"
213274
warnings=$((warnings + 1))
214275
fi
215276

216277
# ── Test site ─────────────────────────────────────────────────────────────────
278+
# Playwright's webServer starts/stops the test site automatically.
279+
# Probe that it's serveable: start in background, hit the endpoint, then stop.
217280
PAGES_URL="${PAGES_HOST:-https://127.0.0.1}${PAGES_HOST_PORT:+:${PAGES_HOST_PORT}}"
218-
PID_FILE="$ROOT_DIR/.test-site.pid"
219281

220-
if curl -sf $CACERT_OPT --max-time 3 "${PAGES_URL}" > /dev/null 2>&1; then
221-
row "$OK" "Test site" "running (${PAGES_URL})"
222-
elif [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
223-
row "$WARN" "Test site" "process alive but not yet responding"
224-
warnings=$((warnings + 1))
282+
_probe_test_site() {
283+
local pid log i ok=0
284+
log=$(mktemp)
285+
286+
# If something is already serving on this port, the server is clearly up
287+
if curl -sf $CACERT_OPT --max-time 2 "${PAGES_URL}" > /dev/null 2>&1; then
288+
rm -f "$log"; return 0
289+
fi
290+
291+
# exec replaces the subshell with node directly, so $pid == node PID
292+
(
293+
export SSL_CERT=${BW_SSL_CERT:-ssl.crt}
294+
export SSL_KEY=${BW_SSL_KEY:-ssl.key}
295+
export SERVE_PORT=$PAGES_HOST_PORT
296+
export SERVE_INSECURE_PORT=$PAGES_HOST_INSECURE_PORT
297+
cd "$ROOT_DIR/test-site/api"
298+
exec node build/app.js
299+
) > "$log" 2>&1 &
300+
pid=$!
301+
302+
for i in $(seq 1 15); do
303+
sleep 0.5
304+
if ! kill -0 "$pid" 2>/dev/null; then break; fi
305+
if curl -sf $CACERT_OPT --max-time 2 "${PAGES_URL}" > /dev/null 2>&1; then
306+
ok=1; break
307+
fi
308+
done
309+
310+
kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null
311+
rm -f "$log"
312+
return $((1 - ok))
313+
}
314+
315+
if _probe_test_site; then
316+
row "$OK" "Test site" "serveable (${PAGES_URL})"
225317
else
226-
row "$WARN" "Test site" "not running (${PAGES_URL})"
227-
hint "npm run start:test-site"
228-
warnings=$((warnings + 1))
318+
row "$ERR" "Test site" "failed to serve"
319+
hint "cd test-site && npm run build:client # rebuild if not yet built"
320+
hint "cd test-site/api && npm run build # rebuild API if needed"
321+
errors=$((errors + 1))
229322
fi
230323

231324
# ── Summary ───────────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)