Severity: Medium
Affected repos:middleware-node
Component boundary: middleware (local mode) → on-disk file
Motivation
Transport in src/core/transport.ts defaults to local mode when apiKey is missing and opens ws://127.0.0.1:9847. The VS Code extension does not host that server and has decided not to — see recost-dev/extension#91. Every default-configured SDK in local mode reconnects forever to nothing.
A file-based transport gives users a working "no cloud account" path without a localhost listener: the SDK writes WindowSummary frames to disk; tooling can read them later.
Proposal
Add a local-file transport mode alongside the existing WS mode.
- Config:
RecostConfig.localTransport: "ws" | "file". Default: file. The WS target does not exist (recost-dev/extension#91), so the new default writes telemetry to disk instead of silently dropping it. ws remains as an opt-in. Behavior change for anyone currently using local mode by accident, but their telemetry was going nowhere — so this is strictly an improvement. - Wire format: append-only NDJSON. Each line is exactly one
JSON.stringify(WindowSummary). Every frame carries a top-level protocolVersion: "1.0" (covers recost-dev/extension#99 for this transport). - Location:
${RECOST_LOCAL_DIR ?? path.join(os.homedir(), ".recost", "local-telemetry")}/${projectId}.jsonl. - Permissions:
0o600 on POSIX; document the Windows ACL limitation. - Failure modes: disk-full / EACCES drops the oldest queued frame, fires
onError once per overflow episode. Never throws. - Lifecycle: flush + close stream on
dispose().
Open for the implementer
- Rotation (per-day? per-size? both?) and retention.
- Multi-process concurrency on the same
projectId (POSIX O_APPEND atomicity vs Windows). - Whether the existing WS transport is eventually removed (separate issue).
Verification
init({ projectId: "proj_x" }) (no localTransport set → uses new default file) + 3 fetch() calls writes 3 NDJSON lines with protocolVersion: "1.0".init({ projectId: "proj_x", localTransport: "ws" }) still attempts the existing WS path.dispose() flushes within 1s.- File mode
0o600 on POSIX.
Out of scope
- Reading the files (no consumer exists — extension explicitly opted out per
recost-dev/extension#91). - Removing the existing WebSocket transport.
- Encryption / signing of on-disk frames.
Related
recost-dev/extension#91, recost-dev/extension#99- Python mirror: cross-referenced in a follow-up comment.
Severity: Medium
Affected repos:
middleware-nodeComponent boundary: middleware (local mode) → on-disk file
Motivation
Transportinsrc/core/transport.tsdefaults to local mode whenapiKeyis missing and opensws://127.0.0.1:9847. The VS Code extension does not host that server and has decided not to — seerecost-dev/extension#91. Every default-configured SDK in local mode reconnects forever to nothing.A file-based transport gives users a working "no cloud account" path without a localhost listener: the SDK writes
WindowSummaryframes to disk; tooling can read them later.Proposal
Add a
local-filetransport mode alongside the existing WS mode.RecostConfig.localTransport: "ws" | "file". Default:file. The WS target does not exist (recost-dev/extension#91), so the new default writes telemetry to disk instead of silently dropping it.wsremains as an opt-in. Behavior change for anyone currently using local mode by accident, but their telemetry was going nowhere — so this is strictly an improvement.JSON.stringify(WindowSummary). Every frame carries a top-levelprotocolVersion: "1.0"(coversrecost-dev/extension#99for this transport).${RECOST_LOCAL_DIR ?? path.join(os.homedir(), ".recost", "local-telemetry")}/${projectId}.jsonl.0o600on POSIX; document the Windows ACL limitation.onErroronce per overflow episode. Never throws.dispose().Open for the implementer
projectId(POSIXO_APPENDatomicity vs Windows).Verification
init({ projectId: "proj_x" })(nolocalTransportset → uses new defaultfile) + 3fetch()calls writes 3 NDJSON lines withprotocolVersion: "1.0".init({ projectId: "proj_x", localTransport: "ws" })still attempts the existing WS path.dispose()flushes within 1s.0o600on POSIX.Out of scope
recost-dev/extension#91).Related
recost-dev/extension#91,recost-dev/extension#99