Fix session lifetime: 2h sliding idle timeout, not 30 days
The JWT's exp claim says 30 days, and I took that as the session
lifetime. It is only an outer ceiling. The server also keeps a per-token
whitelist entry in Valkey (jwt:{accountId}:{jti}) whose TTL is
JWT_TIMEOUT_SECONDS — 7200s on this instance — and JwtStrategy.validate
re-sets it on every authenticated request. Two hours idle and the token
is rejected with 29 days still on exp.
Proven, not inferred: the token from yesterday returned 401 at 13.8h old.
The live instance publishes the values unauthenticated at
GET /api/v3/config/public — JWT_TIMEOUT_SECONDS 7200,
JWT_SHOW_TIMEOUT_WARNING_SECONDS 3600, the latter being exactly the
one-hour UI prompt that prompted this investigation.
refresh-session turns out not to be special: it extends through the same
guard as any other route, and uniquely only in returning the remaining
TTL. So the keepalive uses GET /api/v3/me instead, and the server stays
GET-only; the one POST in the repo is in scripts/probe.mjs, where it
reports the idle budget.
JWT_EXTENDED_TIMEOUT_SECONDS (~1 month) exists in the config schema but
is vestigial: privateDevice has no references in the current NestJS
source, and generateJwtAndAddToWhitelist never overrides the TTL.
Also fixes a real breakage this surfaced: TypeScript parameter
properties are rejected by Node's type stripping, so `npm run dev` and
`npm test` both failed on any file reaching them. Rewritten as explicit
fields, and noted in CLAUDE.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
13
CLAUDE.md
13
CLAUDE.md
@@ -59,7 +59,7 @@ the property that makes that acceptable. Do not add a write tool without the
|
||||
user explicitly asking for one and understanding this.
|
||||
|
||||
**Never log or echo secrets.** `TSC_JWT_COOKIE` grants full read access to the
|
||||
account for 30 days; `MCP_AUTH_TOKEN` guards the endpoint. Neither belongs in
|
||||
account; `MCP_AUTH_TOKEN` guards the endpoint. Neither belongs in
|
||||
logs, error messages, or tool output. `.env` is git-ignored — keep it that way.
|
||||
|
||||
**Live behaviour beats upstream source.** The clones in `vendor/` track `main`
|
||||
@@ -84,11 +84,18 @@ These cost real time to discover; `docs/API.md` has the full list with evidence.
|
||||
its own OpenAPI document. It is not in the main `docs-json`.
|
||||
- Legacy lesson responses return ids as `{buffer:{data:[...]}}`; use
|
||||
`normalizeObjectId`.
|
||||
- **The JWT dies after 2h idle, not 30 days.** `exp` is a hard ceiling; the
|
||||
real limit is a Valkey whitelist entry (`JWT_TIMEOUT_SECONDS`, live value at
|
||||
`GET /api/v3/config/public`) that every authenticated request re-sets.
|
||||
`src/keepalive.ts` holds it open. Do not "simplify" it away.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Imports use `.ts` extensions; `rewriteRelativeImportExtensions` makes `tsc`
|
||||
emit `.js`. This lets `node --watch src/bin/http.ts` run the tree directly.
|
||||
- **No TypeScript parameter properties** (`constructor(private readonly x: T)`).
|
||||
Node's type stripping rejects them, which breaks `npm run dev` and `npm test`.
|
||||
Declare the field and assign it in the constructor body instead.
|
||||
- Tabs for indentation, single quotes, trailing commas.
|
||||
- Comments explain *why* — an API quirk, a security property, a trade-off — not
|
||||
what the line does. Several such comments encode findings that are expensive
|
||||
@@ -108,5 +115,5 @@ These cost real time to discover; `docs/API.md` has the full list with evidence.
|
||||
## Environment
|
||||
|
||||
`.env` holds `TSC_URL`, `TSC_JWT_COOKIE`, `MCP_AUTH_TOKEN`. See `.env.example`
|
||||
for the full set and `docs/AUTH.md` for refreshing the JWT — it expires every 30
|
||||
days, and `npm run probe` reports the days remaining.
|
||||
for the full set and `docs/AUTH.md` for refreshing the JWT. `npm run probe`
|
||||
reports both clocks: days until hard expiry and seconds of idle budget left.
|
||||
|
||||
Reference in New Issue
Block a user