The auth proxy intercepts GET /usage and returns only entries belonging to
the authenticated npub's clients, with owner suffixes stripped. The TUI no
longer needs to filter entries client-side.
- Remove getNpubSuffix and loadConfig from tui/usage/data.ts imports
- Remove old suffix-based filtering logic from fetchUsage()
- Remove unused getNpubSuffix from cli.ts imports
- Add ownerNpub?: string to ClientEntry and DaemonClient interfaces
- Update getClientsFromStore() to preserve ownerNpub field
- GET /clients: include ownerNpub in daemon response
- POST /clients/add: accept and store ownerNpub from auth proxy
- Remove getNpubSuffix/addSuffixToId/removeSuffixFromId from clients.ts (moved to auth proxy)
- Remove CLI-side suffixing in addDaemonClient and deleteClientAction
- Remove config loading from getClientsList (no longer needed for filtering)
- CLI output now shows raw client IDs without suffix stripping
When multiple routstrd processes (or CLI auto-starts) bootstrap simultaneously,
each process found cocod/routstrd unreachable, spawned its own copy, and
contended for the same database socket. The paired ~200ms burst timestamps
in the logs were the symptom.
Fixes applied:
- Add cross-process startup lock (process-lock.ts)
- Uses atomic mkdir as the lock primitive.
- Stores PID + UUID token; verifies ownership on release to avoid
removing a lock taken by a new process after the original crashed.
- Removes stale locks (unreachable PID or lock older than staleAfterMs).
- Exports withCrossProcessLock() for simple RAII-style usage.
- Fix cocod startup race (cocod-client.ts)
- Wrap ensureDaemonRunning() in a socket-derived lock path.
- Re-ping inside the lock before spawning; if another process just started
cocod, the current process detects it and connects instead of spawning.
- Switch from 'cocod daemon' to 'cocod init' for cleaner daemonization.
- Allow exit code 0 from 'cocod init' (normal daemon exit without error).
- Consolidate routstrd daemon startup (daemon-client.ts)
- startDaemonProcess() now delegates to startDaemon() from start-daemon.ts,
sharing the same lock and health-check logic.
- Add cross-process lock to routstrd daemon startup (start-daemon.ts)
- Performs pre-lock isDaemonHealthy() check to skip lock entirely when
already running.
- Performs re-check inside the lock before spawning to avoid duplicate
daemon processes.
- Added 'routstrd refresh' command that:
- Calls /v1/models?refresh=true to refresh routstr21 models
- Calls runIntegrationsForClients() for all registered clients
- Fixed type signature in refreshModelsAndIntegrations (returns any[] not void)
- Imports getClientsList and runIntegrationsForClients in cli.ts
- Created reusable refreshModelsAndIntegrations() function that combines
model refresh with client integration refresh
- Updated src/daemon/index.ts to use the new function in both the scheduled
refresh job and the initial bootstrap refresh
- Merged two chained .then() calls into one in the initial bootstrap flow
- Removed redundant imports (runIntegrationsForClients, getClientsList) from
daemon/index.ts since they're now encapsulated in the function
- Add addSuffixToId() and removeSuffixFromId() helper functions
- Only suffix client IDs, never names
- Update getClientsList, deleteClientAction, and addClientAction to use helpers
Replace ensureDaemonClient (try-create → catch 'already exists' → fetch list) with
a single addDaemonClient that fetches the list first, checks if client exists,
then creates only if needed. Returns { client, created } in all cases.
- Moved addDaemonClient and ensureDaemonClient from daemon-client.ts to clients.ts
- Moved clients list/delete/add action handlers from cli.ts to clients.ts
- Updated cli.ts to call the new action functions from clients.ts
- Updated integrations/index.ts to import ensureDaemonClient from clients.ts
- Fixed circular dependency by having clients.ts import from ../integrations/registry instead of ../integrations
Replace direct store.getState().clientIds access with a shared utility:
- getClientsFromStore(store) for daemon/local usage
- getClientsList() for remote CLI usage via daemon API
This ensures integration refresh logic and client management work
consistently whether running inside the daemon or connecting to a
remote daemon where store access is unavailable.
Updated:
- src/daemon/index.ts: use getClientsFromStore for integration refresh
- src/daemon/http/index.ts: use getClientsFromStore in /clients endpoints
- src/utils/daemon-client.ts: use getClientsList in ensureDaemonClient
- src/cli.ts: use getClientsList in clients list command
- Move get-or-create client logic from integrations/index.ts into
ensureDaemonClient() in daemon-client.ts as a generic utility.
- Update setupIntegration() and the CLI clients add --<integration>
paths to use the new helper.
- Return { client, created } so callers can decide whether to log.
- Remove IntegrationClient alias and ensureIntegrationClient export.
- Add shared DaemonClient type and addDaemonClient() helper in
daemon-client.ts as the single source for POST /clients/add.
- Update ensureIntegrationClient() to use the new helper.
- Update CLI clients add command to use the new helper.
- Keeps existing error handling and 'already exists' fallback in
ensureIntegrationClient().
- fetchUsage now filters entries to only those whose client ID ends with
our npub suffix when running in remote daemon mode (daemonUrl + nsec)
- strips the suffix from client names before display, matching CLI behavior
- local mode unchanged: shows all usage entries
- clients add: appends _<npub_suffix> to name when daemonUrl+nsec are set
- clients list: filters to only clients matching our npub suffix, strips suffix for display
- clients delete: auto-appends suffix to id before sending if not already present
- adds getNpubSuffix() helper in daemon-client.ts
- clients add --pi-agent/--opencode/--openclaw/--claude-code now call
/clients/add on the daemon instead of writing to local Bun SQLite
- add ensureIntegrationClient helper that creates via daemon and falls
back to fetching existing client on 409
- update install*Integration signatures to accept apiKey instead of SdkStore
- update onboard setupIntegration to use daemon-backed path
- update daemon scheduled refresh to pass apiKeys directly
- Add normalizeNostrPubkey, npubFromPubkey, npubFromSecretKey to nip98.ts
- Extend callDaemon to accept DELETE method
- Add subcommands:
- list: GET /npubs
- add <npub>: POST /npubs (accepts hex pubkey or npub1...)
- delete <npub>: DELETE /npubs/:npub (accepts hex pubkey or npub1...)
- All npubs commands go through callDaemon, so they work with
remote daemons and are automatically NIP-98 signed
- Export getDaemonBaseUrl from daemon-client for shared URL resolution
- Replace all hardcoded http://localhost: in client integrations
(opencode, claudecode, openclaw, pi) with getDaemonBaseUrl(config)
- Replace raw fetch(http://localhost:/models) with callDaemon("/models")
so model fetching works with remote daemons and gets NIP-98 signed
- Fix clients add command output to print the actual daemon base URL
instead of hardcoded localhost
- Add nostr-tools dependency for NIP-98 event signing
- Add nsec field to RoutstrdConfig for remote daemon authentication
- Extract NIP-98 auth utilities into src/utils/nip98.ts (parseSecretKey,
createNIP98Authorization with SHA-256 payload tags)
- Wire NIP-98 Authorization headers into callDaemon and isDaemonRunning
whenever both daemonUrl and nsec are configured
- Refactor refund command to use callDaemon instead of hardcoded localhost
so it works correctly with remote daemon URLs
- Add optional daemonUrl field to RoutstrdConfig
- Update callDaemon and isDaemonRunning to use configurable base URL
- Disable auto-start when daemonUrl is set (Option A)
- Gate local-only commands: onboard, start, restart, mode, logs, service install
Allow overriding the default cocod config directory (~/.cocod)
via the COCOD_DIR environment variable. Falls back to existing
HOME/USERPROFILE logic when unset.
Bump version to 0.2.6.