Main#4
Conversation
There was a problem hiding this comment.
💡 Codex Review
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/migrations/0001_init_control_plane.sql#L138-L140
Avoid replaying a conflicting 0001 migration
The checked migration runner apps/api/scripts/migrate.mjs sorts and applies every .sql file, so on a fresh DB it applies the existing 0001_control_plane_init.sql first, which creates organizations.id and projects.id as TEXT. This new migration then reaches usage_events.organization_id UUID REFERENCES organizations(id), and PostgreSQL rejects that FK because a UUID column cannot reference a TEXT primary key, aborting migrations before the v0.4 tables are usable.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/src/server.ts#L273-L275
Keep usage reads scoped to the API key customer
When GET /api/v1/usage/events is called with any active customer API key, the authenticated key is discarded and customerId is trusted from the query string. A key for customer A can omit this parameter to read all usage events, or set customer B's id to read B's events; the summary endpoint builds the same filter object, while the POST path above correctly records events under apiKey.customerId.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/src/server.ts#L324-L330
Keep asset reads scoped to the API key customer
With any active API key, the asset list endpoint lets the caller choose customerId or omit it, so a key for one customer can list other customers' assets. The by-id asset handlers below have the same ownership gap because they only call getAsset/deleteAssetRecord by id after authentication; these reads/deletes should be constrained to the authenticated apiKey.customerId that is used when creating assets.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/package.json#L17-L19
Point root database scripts at existing API scripts
Running the root migrate path dispatches to pnpm --filter @stacklane/api db:migrate, but apps/api/package.json defines migrate and seed, not db:migrate or db:seed. In that setup the root migration/seed commands fail before invoking the API package scripts, so either add matching package script names or call the existing migrate/seed scripts here.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/src/server.ts#L251-L252
Allow auth headers for browser API-key calls
These new API-key protected endpoints are meant to be called with Authorization or x-api-key (the SDK sends Authorization), but the shared OPTIONS response still allows only content-type. In a browser/cross-origin app under WEB_ORIGIN, the preflight for usage/assets calls includes the auth header and fails before reaching requireLocalApiKey, so those SDK calls only work from same-origin or server-side contexts.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/src/server.ts#L318
Accept the documented asset upload field
The docs/API.md contract for asset uploads and the Fastify route added in this commit use dataBase64, but the active server only reads bytesBase64 here. A client following the documented payload gets a 201 asset with sizeBytes 0 and no file persisted, because the upload body is silently ignored.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/packages/sdk/src/index.ts#L111
Point SDK project routes at served API paths
The active API process started by @stacklane/api is src/server.ts, which registers control-plane project routes as /projects; the /v1/projects Fastify routes added under src/modules are not wired into start/buildApp. Consumers using these SDK project methods therefore call paths that fall through to auth/404 instead of creating, listing, or loading projects.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/src/server.ts#L214
Require auth for customer and key administration
These customer/API-key management routes are registered before the later requireUser gate and they also do not require an existing API key. In any reachable API deployment, an unauthenticated caller can create customers and mint active keys, list key records, or revoke keys, which bypasses the protection added around usage and asset endpoints.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/apps/api/src/server.ts#L346-L348
Remove persisted files when deleting assets
When an asset is created with file bytes, createAssetRecord saves a file under .stacklane/files and stores its storagePath, but this delete path only removes the JSON asset record. Deleting uploaded assets therefore leaves hidden orphan files on disk, so repeated create/delete cycles continue consuming local storage even though the API reports the asset as deleted.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/packages/cli/src/index.ts#L282
Require a customer when creating CLI keys
The command is described as creating a customer API key, but --customer is optional and the record is written with customerId: opts.customer. Running the documented stacklane keys create without that flag creates an active key with no tenant/customer boundary, so later authenticated usage or asset writes are stored unassigned instead of scoped to a customer.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/packages/cli/src/index.ts#L318
Validate CLI usage units before writing events
The HTTP API rejects missing, non-finite, and non-positive usage units, but the CLI persists Number(opts.units) directly. A typo such as --units abc is serialized as null, and --units -5 records negative usage, which corrupts local summaries and any later usage-to-charge mapping built from these files.
https://github.com/talocode/stacklane/blob/959176481b932b733ca04e460868b5e87da1b6a5//tmp/stacklane-review-069/packages/cli/src/index.ts#L304-L306
Fail CLI key revoke when no key matches
This command always writes the mapped list and prints a successful revocation even when opts.id did not match any key. If an operator mistypes or copies the wrong id while revoking a compromised key, the key remains active while the CLI reports success, so the caller has no signal to retry with the correct id.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Add @stacklane/mcp v0.4.1: an open-source MCP server that exposes Stacklane v0.4.1 backend/usage/storage primitives to MCP-compatible agents (Codex, Claude Code, OpenCode, Cursor) over stdio transport. Local-first only. - 17 MCP tools: health, config, customers, API keys, usage, assets - strict zod input schemas for every tool - raw API key returned only from stacklane_create_api_key; key hashes never returned by list/revoke/verify tools - asset tools reject unsafe filenames, null bytes, path traversal, absolute paths, and path separators - secrets redacted from errors; no stack traces exposed - no arbitrary shell execution, no billing, no cloud provisioning - only external network call is the configured STACKLANE_MCP_BASE_URL - depends on @modelcontextprotocol/sdk (open-source) and zod; no Supabase, no Resend, no external platform dependency - bin entry stacklane-mcp -> dist/index.js (private package, not published) - docs/MCP.md, examples/mcp/* configs and sample prompts - scripts/test-stacklane-mcp-v010.mjs with mock API integration - root test:mcp script All validations pass: lint, typecheck (8 packages), build, v010/v020/v040/ v041 runtime tests, MCP v0.1 tests, and @stacklane/api test suite. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
OpenAI-compatible AI provider routing layer with: - POST /v1/chat/completions — routes through configured providers - GET /v1/models — lists talocode/auto, talocode/fast, talocode/coding - GET /api/v1/cloud/router/providers — configured provider list - GET /api/v1/cloud/router/health — health check Provider abstraction supports OpenAI, OpenRouter, Gemini, Mock with automatic fallback on 429/5xx/timeout. Prepaid wallet charging: pre-charge before call, delta charge after. Usage events recorded with product=talocode_router. Context compression (logs/diff/trace modes) enabled via TALOCODE_ROUTER_ENABLE_COMPRESSION=true. Pricing: talocode_router product added to central catalog. All existing billing/dashboard routes preserved.
- Add credits override to chargeCredits for dynamic-cost routing - Fix router fallback to always include mock provider - Fix router fallback to try next provider instead of throwing on non-fallback errors - Remove hardcoded dark-only CSS variables, add light/dark theme vars to layout - Add next-localhost.cjs launcher for uv_interface_addresses permission fix - Add smoke scripts for billing and router validation - Add chat.completions pricing entry
No description provided.