Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .claude/development/smoke-prod-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ with 2s backoff per call. Exit codes: `0` ok, `1` fail, `2` skipped.

| ID | What it checks | Trigger |
|----|----------------|---------|
| `health` | `GET $SUPABASE_URL/functions/v1/health` returns 200 | always-on canary |
| `health` | `GET $SUPABASE_URL/functions/v1/health` returns 200; `verbose=true` body's `redis` check must be `healthy` | always-on canary |
| `website-device-page` | `GET https://www.skillsmith.app/device` returns 200 + contains `data-smoke="device-input"` | `packages/website/src/pages/device.astro` change |
| `edge-fn-auth-device` | `auth-device-code` POST → 200/400 (NOT 404); `auth-device-preview` GET (no JWT) → 401 | `supabase/functions/auth-device-{code,preview,approve,token}/**` change |
| `edge-fn-skills-search` | Authenticated GET with `X-API-Key: $SMOKE_SKILLS_API_KEY` → 200; asserts `user_api_usage.search_count` incremented by 1 | `supabase/functions/skills-search/**` or `_shared/usage-counter.ts` / `_shared/auth-middleware.ts` change |
Expand Down Expand Up @@ -107,6 +107,11 @@ Triage steps:
or hotfix.
- Assertion is wrong (smoke-harness bug) → fix the check function;
deploy is fine.
- `redis` check present but not `healthy` → real Upstash issue (bad/rotated
token, network, quota); see docs/internal/runbooks/upstash-redis-operations.md.
Not a full outage by itself — rate-limiter/quota-enforcer/tier-cache/
circuit-breaker all degrade gracefully, but treat as urgent (rotation
may be needed).

## Skipping smoke for a PR

Expand Down
35 changes: 35 additions & 0 deletions .env.schema
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,41 @@ GA4_MEASUREMENT_ID=
# @type=string @required=false @sensitive
GA4_API_SECRET=

# =============================================================================
# UPSTASH REDIS (RATE LIMITING / CACHING) — SMI-1231
# =============================================================================

# Upstash Redis REST URL - Backs rate-limiter.ts (sliding-window rate limits),
# quota-enforcer.ts and tier-cache.ts (look-aside caches in front of Postgres
# RPCs, self-healing on failure), and circuit-breaker.ts (distributed trip
# state) — one instance, four consumers. All four fail open to an in-memory,
# per-isolate fallback if this is unset/unreachable. rate-limiter.ts is the
# one module where that matters: a per-isolate in-memory counter isn't
# shared across concurrent Supabase edge instances, so a silent outage
# measurably weakens (not disables) per-tier rate enforcement. Full
# architecture + rotation: docs/internal/runbooks/upstash-redis-operations.md
# Get from: Upstash Console → Redis → (database) → REST API → UPSTASH_REDIS_REST_URL
# Staging may omit this entirely (graceful degradation) — see
# docs/internal/implementation/supabase-staging-environment.md
# @type=string(startsWith=https://) @required=false @sensitive=false
UPSTASH_REDIS_REST_URL=

# Upstash Redis REST Token - Paired with UPSTASH_REDIS_REST_URL above. MUST
# be a read+write token: rate-limiter increments counters, quota-enforcer and
# tier-cache write cache entries, circuit-breaker writes trip state. Upstash's
# read-only token option is NOT viable here — do not scope this down in a
# future least-privilege pass (see upstash-redis-operations.md).
# Get from: Upstash Console → Redis → (database) → REST API → UPSTASH_REDIS_REST_TOKEN
# @type=string @required=false @sensitive
UPSTASH_REDIS_REST_TOKEN=

# Rate Limit Fail-Closed Mode - When "true", checkRateLimit() in
# rate-limiter.ts rejects ALL requests if Redis is unreachable/misconfigured,
# instead of falling back to an in-memory limiter. Default (unset/"false")
# is fail-open. Registered guard: docs/internal/process/guards-and-opt-outs.md.
# @type=enum(true,false) @required=false @sensitive=false
RATE_LIMIT_FAIL_CLOSED=

# =============================================================================
# CLOUDINARY MEDIA MANAGEMENT
# =============================================================================
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Detailed guides extracted via progressive disclosure. CLAUDE.md contains essenti
| [edge-function-attribution-queries.md](.claude/development/edge-function-attribution-queries.md) | Canonical pooler queries for edge function attribution monitoring (SMI-4370 / Wave 4d) |
| [concurrency-patterns.md](.claude/development/concurrency-patterns.md) | Pattern-to-incident-to-canonical-fix index for the five `concurrency-auditor` patterns (SMI-4895/4896/4861/4887) |
| [guards-and-opt-outs.md](docs/internal/process/guards-and-opt-outs.md) | Canonical Guards & Opt-Outs registry (guard → trigger → marker → disable var → scope) — SMI-5418 DoD #5 |
| [upstash-redis-operations.md](docs/internal/runbooks/upstash-redis-operations.md) | Upstash Redis architecture (4 consumers), setup, credential rotation, health-check monitoring |

**Implementation plan template**: [.claude/templates/implementation-plan.md](.claude/templates/implementation-plan.md) — use this structure for all plans in `docs/internal/implementation/`.

Expand Down
2 changes: 1 addition & 1 deletion docs/internal
Binary file modified supabase/functions/_shared/health-checks.helpers.test.ts
Binary file not shown.
Binary file modified supabase/functions/_shared/health-checks.probes.test.ts
Binary file not shown.
Binary file modified supabase/functions/_shared/health-checks.probes.ts
Binary file not shown.
Binary file modified supabase/functions/_shared/health-checks.readers.test.ts
Binary file not shown.
Binary file modified supabase/functions/_shared/health-checks.ts
Binary file not shown.
Binary file not shown.
Binary file added supabase/functions/_shared/rate-limiter.health.ts
Binary file not shown.
Binary file modified supabase/functions/_shared/rate-limiter.ts
Binary file not shown.
Binary file modified supabase/functions/health/index.test.ts
Binary file not shown.
Binary file modified supabase/functions/health/index.ts
Binary file not shown.
Binary file modified supabase/functions/status-check/index.test.ts
Binary file not shown.
Binary file modified supabase/functions/status-check/index.ts
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Rollback for 20260719030000_status_component_rate_limiting.sql
-- SMI-5768
--
-- Removes the 'rate-limiting' status_components row. Cascades (ON DELETE
-- CASCADE, migration 20260719010000_status_page_schema.sql) to any
-- status_checks/status_daily_rollups rows already accumulated for this
-- component — that history is permanently lost, same tradeoff as the other
-- 6 components' rollback would carry.
--
-- Safe to run standalone via:
-- docker exec skillsmith-dev-1 varlock run -- ./scripts/pooler-psql.sh -f supabase/rollbacks/20260719030000_status_component_rate_limiting_down.sql

DELETE FROM status_components WHERE slug = 'rate-limiting';
Loading