返回 Skills
insforge/agent-skills· Apache-2.0 内容可用

insforge-debug

>-

安装

与 skills.sh 相同的 Command / Prompt 安装方式


name: insforge-debug description: >- Use when diagnosing problems in an InsForge project — reactive failures (SDK error object, HTTP 4xx/5xx, gateway timeout 502/503/504, edge function failure or timeout, login/OAuth/auth errors, RLS denial, realtime channel issues, slow query on one endpoint, edge function or Vercel deploy failure), proactive audits (security/RLS review, performance/index review, system health check, pre-launch readiness), or when the user has an error but doesn't know where to start. license: Apache-2.0

InsForge Debug

Diagnose problems in InsForge projects by combining the backend's observability primitives — logs, metrics, db-health, advisor, policies, metadata, error objects, deploy state, and AI assist. This skill provides:

  1. A reference per debug primitive (one observability surface each — under references/)
  2. Symptom Recipes (below) that name the primitive sequence for known reactive symptoms and proactive audits

Always use npx @insforge/cli — never install the CLI globally.

Fastest Path: AI-Assisted Triage

When the user gives a concrete description (error message, failing URL, HTTP status), hand it to the InsForge debug agent. Unlike the other primitives, this one returns suggestions, not just observations — verify the diagnosis against the primitives it cites before acting on it.

npx @insforge/cli diagnose --ai "<issue description>"

See references/ai-assisted.md for when to use this first vs when to skip, and how to verify the output.

Debug Primitives

Each primitive is one independently-queryable observability surface backed by a distinct underlying data source. Real diagnoses are compositions of primitives.

All commands run via npx @insforge/cli .... The (command) shown next to each primitive is the actual CLI command — primitive names are concept labels, not CLI subcommand names (e.g., "DB health" is diagnose db, not diagnose db-health; "Policies" is db policies, not diagnose policies).

Primitive (command)What you seeReference
Logs (logs <source>; diagnose logs for cross-source aggregate)Time-stream of events from 5 backend sources (insforge.logs / postgREST.logs / postgres.logs / function.logs / function-deploy.logs)references/logs.md
Metrics (diagnose metrics)EC2 instance time-series (CPU / memory / disk / network) over 1h / 6h / 24h / 7dreferences/metrics.md
DB health (diagnose db)Current Postgres state via 7 named checks (connections / slow-queries / bloat / size / index-usage / locks / cache-hit)references/db-health.md
Advisor (diagnose advisor --json)Static-scan issues across 3 categories (security / performance / health) with ruleId / affectedObject / recommendationreferences/advisor.md
Policies (db policies)Active RLS rules from pg_policies (USING / WITH CHECK per cmd per role) — returns all policies as a dumpreferences/policies.md
Metadata (metadata --json)Declarative backend state dump (auth config / tables / buckets / functions / AI models / realtime channels)references/metadata.md
Error objects (no command — read SDK / HTTP response)SDK error envelope + HTTP status — the routing table from a client-visible error to the right log sourcereferences/error-objects.md
Deploy state (deployments list + deployments status <id> --json + logs function-deploy.logs)Frontend (Vercel) deployment history + per-deploy metadata, plus edge function deploy logsreferences/deploy-state.md
AI assist (diagnose --ai "<description>")LLM agent that combines the other primitives — returns a diagnosis with suggestionsreferences/ai-assisted.md

Symptom Recipes

Each recipe is a primitive call sequence with one-line "look for X" at each step. Command syntax, flags, and deep interpretation are in the per-primitive references above.

Recipe: SDK returned { data: null, error: { code, message } }

  1. error-objects — read code/message/details. If code starts with PGRST*, route by prefix using the table in the reference.
  2. logs (matching source per error-objects routing) — find the error timestamp, get the full backend-side context.
  3. db-health (connections, locks, slow-queries) — only if the error suggests DB issue (PostgREST timeout, lock conflict).

Recipe: HTTP 4xx/5xx response on a specific request

  1. error-objects — use the HTTP status routing table to pick the log source (each status has a distinct path; 429 is special).
  2. logs (right source for that status) — find the failing request line and error.
  3. metrics — only for 5xx patterns spanning multiple endpoints, to confirm system-wide load issue.

Recipe: RLS access issue (403 on write, or empty result on read)

Same bug, two surfacings. Writes (INSERT / UPDATE / DELETE) fail loudly with 403. Reads (SELECT) fail silently with an empty array — PostgREST filters denied rows out instead of returning 403, so the request looks successful with zero rows. Diagnosis path is the same except step 1 only applies to the 403 variant.

  1. logs (postgREST.logs) — 403 variant only: find the policy violation event with table and role context. Empty-result variant: skip — no error is logged for silently-filtered rows.
  2. policies — list policies for that table; walk USING / WITH CHECK against the actual request and the JWT claim used.
  3. metadata — verify auth config (which claim feeds auth.uid() / requesting_user_id(); for third-party auth like Clerk/Auth0, is the provider registered as a JWT issuer?).
  4. db query (db query "<sql>") — empty-result variant only: confirm rows that should be visible actually exist by querying as service role (not as the user): npx @insforge/cli db query "SELECT id, user_id FROM <table>". Distinguishes "RLS filtered everything" from "no matching data exists".

Recipe: Login fails / OAuth callback errors / token expired

  1. logs (insforge.logs) — find auth errors with timestamp and provider context.
  2. metadata — verify the provider is enabled, redirect URLs match the callback URL exactly (protocol + host + path).

Recipe: Edge function runtime error / timeout

  1. logs (function.logs) — get the error stack and execution context.
  2. metadata — confirm the function exists and status: "active".
  3. (If needed) npx @insforge/cli functions code <slug> — inspect the source for obvious issues.

Recipe: functions deploy failed

  1. deploy-state (function-deploy.logs) — find the build/push error.
  2. metadata — confirm whether the function ended up in the active list (partial-deploy detection).

Recipe: deployments deploy failed (Vercel)

  1. deploy-state (deployments list + status <id> --json) — read status, metadata.webhookEventType, and envVarKeys.
  2. Local npm run build — reproduce the same error locally for faster iteration.

Recipe: Single slow query / one endpoint slow

  1. logs (postgres.logs) — find the query text and timestamp.
  2. db-health (slow-queries, index-usage) — confirm it's in pg_stat_statements; check for missing index.
  3. policies — if it's an RLS-gated table, verify the policy isn't adding hidden joins.

Recipe: All responses slow / high CPU/memory (active incident)

  1. metrics (--range 1h) — confirm system-wide pressure (CPU / memory / disk).
  2. db-health — DB is the most common bottleneck; check connections, locks, slow-queries.
  3. logs (diagnose logs aggregate) — error patterns across sources at the spike timestamp.
  4. advisor (--severity critical) — pre-existing known issues that may explain the degradation.

Recipe: Realtime channel won't connect / messages missing

  1. logs (insforge.logs) — WebSocket errors and subscription failures.
  2. metadata — verify the channel pattern matches what the client subscribes to, enabled: true.
  3. policies — RLS on the underlying table (realtime delivers row changes; RLS gates which rows the subscriber sees).

Recipe: 429 rate limit

  1. error-objects — confirm 429 status. No logs are recorded for 429s; no Retry-After header is returned. Don't waste time grepping logs.
  2. metrics (--range 1h) — overall backend load context.
  3. Fix is always client-side: debounce, batch, exponential backoff, eliminate retry loops.

Recipe: Gateway timeout (502 / 503 / 504) on a specific URL

Route by URL subsystem before drilling:

URL patternDrill into
/api/database/records/...logs (postgREST.logspostgres.logs) + db-health (locks, slow-queries)
/functions/<slug>logs (function.logs) — function may be crash-looping
/api/auth/...logs (insforge.logs)
Any path during system-wide spikemetrics (--range 1h)

Recipe: Pre-launch / proactive audit

Requires Platform login (npx @insforge/cli login). Not available when the project is linked via --api-key — fall back to db-health + policies + metadata for a manual audit in that case.

  1. advisor — full scan, then --severity critical first, then warnings.
  2. advisor (--category security) — focus on security issues; cross-verify with policies (RLS coverage) and metadata (auth config, public buckets, secret presence).
  3. advisor (--category performance) — cross-verify with db-health (slow-queries, index-usage, bloat).
  4. advisor (--category health) — cross-verify with metrics (resource trends over 7d).
  5. After fixes, re-run advisor and confirm isResolved: true for each addressed ruleId.

Recipe: Don't know where to start

  1. ai-assisted (diagnose --ai "<error or URL>") — get a starting hypothesis.
  2. Verify by re-checking the primitives the diagnosis names. Trust the primitive observations over the suggestion.

附带文件

agents/openai.yaml
interface:
  display_name: "InsForge Debug"
  short_description: "Diagnose InsForge backend errors and run health audits."
  brand_color: "#17B26A"
  default_prompt: "Use $insforge-debug to diagnose this backend error."
references/advisor.md
# Advisor

Static-scan engine that audits the project against a rule catalog and returns issue rows. The primary primitive for **proactive audits** — security misconfigurations, performance regressions, and system health concerns surfaced **without a specific failing request**.

## Command

```bash
npx @insforge/cli diagnose advisor [--severity critical|warning|info] [--category security|performance|health] [--limit <n>] [--json]
```

Default limit: 50. Requires Platform login — **not available on backends linked via `--api-key`**.

**Add `--json` when you need the full issue payload.** Human-readable output is a 4-column table (severity / category / affectedObject / title). Fields like `ruleId`, `description`, `recommendation`, `isResolved` are only emitted by `--json` — without the flag, you can't read or act on the recommendation programmatically.

## What you see

Each scan returns:

**Scan summary** — `scanId`, `scannedAt`, status, and counts by severity (critical / warning / info).

**Issue rows** — each issue has:

| Field | Meaning |
|-------|---------|
| `ruleId` | Stable identifier of the rule that fired (e.g., `security.rls.missing-policy`) |
| `severity` | `critical` / `warning` / `info` |
| `category` | `security` / `performance` / `health` |
| `affectedObject` | The specific schema object (table, function, policy, secret name) the issue applies to |
| `title` | Short human label |
| `description` | What the rule detected |
| `recommendation` | Suggested fix, often a concrete SQL/CLI step |
| `isResolved` | `true` after a re-scan confirms the fix |

## Categories

| Category | Typical rules | Fix in |
|----------|--------------|--------|
| `security` | RLS missing/permissive on a table, expired/exposed secrets, weak JWT config, public bucket on sensitive data | `db migrations` (RLS), `secrets` (key rotation), `metadata` (auth/bucket config) |
| `performance` | Missing index on heavy filter column, bloat over threshold, sequential scans on large tables, slow recurring query | `db migrations` (indexes), query rewrite, vacuum tuning |
| `health` | Connection pool near limit, EC2 disk filling, deprecated feature in use, version drift | Infra resize, code change, version upgrade |

## How to read

1. **Start with severity**: `--severity critical` first; critical issues block launch.
2. **Group by category** to keep the fix mode coherent (don't context-switch between RLS edits and index migrations).
3. **`affectedObject` tells you where to fix** — it names the concrete schema object.
4. **`recommendation` is usually actionable as-is**. Verify it makes sense (the recommendation may be generic), then apply via the appropriate primitive's tooling.

## Iteration workflow

```text
1. Scan:    diagnose advisor --severity critical --json
2. Triage:  pick one issue, read affectedObject + recommendation
3. Verify:  inspect the affected object (db query / db policies / metadata)
4. Fix:     apply the change (migration / RLS edit / secret rotation / config update)
5. Re-scan: diagnose advisor --json — confirm isResolved=true for that ruleId
6. Repeat with next critical, then warnings, then info
```

## Boundaries

- **Scans are not real-time.** A new scan triggers when the platform schedules it; recommendations lag behind very recent changes. Force a fresh scan if needed.
- **Recommendations are static suggestions, not auto-fixes.** Always validate against current schema state before applying.
- **`affectedObject` is a string, not a typed reference.** It names the object but doesn't link to it — combine with [metadata](metadata.md) / [policies](policies.md) to inspect.
- **Not available when linked via `--api-key`.** Requires `insforge login` (Platform auth).

## Example

Pre-launch audit:

```bash
# Full scan, critical only first
npx @insforge/cli diagnose advisor --severity critical

# Security focus
npx @insforge/cli diagnose advisor --category security

# Performance focus (often pairs with db-health to verify)
npx @insforge/cli diagnose advisor --category performance
npx @insforge/cli diagnose db --check slow-queries,index-usage

# Re-scan after fixes
npx @insforge/cli diagnose advisor --severity critical
```

## Frequently paired with

- [policies](policies.md) — security category issues often name a table with missing/broken RLS policy
- [metadata](metadata.md) — security/health issues often name a configured object (bucket, secret, auth provider) whose state needs inspecting
- [db-health](db-health.md) — performance category overlaps with `slow-queries` / `index-usage` / `bloat`; cross-verify
- [metrics](metrics.md) — health category issues (pool exhaustion, disk fill) line up with metric trends
references/ai-assisted.md
# AI assist

The meta-primitive: hand a natural-language problem description to a backend-side LLM agent that combines the other primitives ([logs](logs.md), [metrics](metrics.md), [db-health](db-health.md), [advisor](advisor.md), [policies](policies.md), [metadata](metadata.md)) on its own and returns a diagnosis plus suggested solutions.

**Unlike every other primitive in this skill, `diagnose --ai` returns suggestions, not just observations.** Verify before acting.

## Command

```bash
npx @insforge/cli diagnose --ai "<issue description>"
```

The description should include: the error / failing URL / HTTP status / function slug — whatever concrete signal the user has.

## When to use first

- User pasted an error, request URL, or status code and asks "why?"
- You want a fast first pass before deciding which primitive to drill into
- The problem spans multiple subsystems (frontend + backend + database) and the right starting primitive isn't obvious

## When to skip

- The symptom clearly maps to one primitive (e.g., "Vercel deploy failed" → go straight to [deploy-state](deploy-state.md))
- You're doing a proactive audit (no concrete error — that's [advisor](advisor.md))
- You already know exactly which log source has the error

## How to verify the output

The agent's diagnosis names primitives and observations. Re-check each:

| If the diagnosis says... | Verify with... |
|--------------------------|----------------|
| "An RLS policy is blocking the request" | [policies](policies.md) — read the actual policy on that table |
| "Slow query on table X" | [db-health](db-health.md) `slow-queries` + [logs](logs.md) `postgres.logs` for the actual query |
| "Function is timing out" | [logs](logs.md) `function.logs` — read the actual timeout/error stack |
| "Connection pool exhausted" | [db-health](db-health.md) `connections` — confirm the count and idle-in-transaction state |
| "Missing index on column Y" | [db-health](db-health.md) `index-usage` + [advisor](advisor.md) performance — both should agree |

If the verification disagrees with the diagnosis, **trust the primitive observation**, not the suggestion. Suggestions can be plausible-sounding but wrong (LLM may pattern-match on similar errors); raw `pg_stat` numbers and log lines can't lie.

## Boundaries

- **Returns suggestions, not just data.** Different from every other primitive — treat the output as a starting hypothesis, not a verdict.
- **Doesn't replace [advisor](advisor.md).** Advisor surfaces issues based on a static rule catalog; `--ai` reasons about a specific reported symptom. They serve different goals.
- **Consumes the other primitives.** When this fails or seems off, fall back to the primitives directly.

## Example

User pastes: "I invoked `https://kttprzh4.functions.insforge.app/newton` and got `508: Loop Detected (LOOP_DETECTED). Recursive requests to the same deployment cannot be processed.`"

```bash
npx @insforge/cli diagnose --ai "I invoked edge function https://kttprzh4.functions.insforge.app/newton, got error: 508: Loop Detected (LOOP_DETECTED)\n\nRecursive requests to the same deployment cannot be processed."
```

Read the diagnosis and suggestions, then verify with:

```bash
# Verify with function logs
npx @insforge/cli logs function.logs --limit 50

# Verify the function code doesn't actually call itself
npx @insforge/cli functions code newton
```

## Frequently paired with

- All other primitives — `--ai` consumes them and you verify back against them. Treat AI assist as a router that points at primitives; the primitives are the ground truth.
references/db-health.md
# DB health

Postgres system views (`pg_stat_*`, `pg_locks`, `pg_class`) exposed as named checks. The primary primitive for **current database state** — connection pool, query performance, lock contention, storage, index efficacy.

## Command

```bash
npx @insforge/cli diagnose db [--check <checks>]
```

`--check` accepts comma-separated names. Default `all`. Run no-arg first when triaging unknown DB issues.

## Checks

| Check | Underlying view | What it tells you |
|-------|-----------------|-------------------|
| `connections` | `pg_stat_activity` | Active vs idle vs idle-in-transaction count; how close to pool limit |
| `slow-queries` | `pg_stat_statements` | Queries by total/mean exec time; which queries dominate load |
| `bloat` | `pg_class`, `pg_stats` | Table/index dead-row bloat (vacuum lag, write-heavy tables) |
| `size` | `pg_class` size functions | Table and index disk footprint |
| `index-usage` | `pg_stat_user_indexes` | Indexes that are never scanned (unused) vs heavy hitters |
| `locks` | `pg_locks` + `pg_stat_activity` | Lock contention, blocking queries, deadlock candidates |
| `cache-hit` | `pg_statio_*` | Buffer cache hit ratio (low = working set exceeds RAM) |

## How to read

| Reading | Likely problem | Next step |
|---------|---------------|-----------|
| `connections` near pool limit + many idle-in-transaction | Connection leak in client code | Find client missing `release()` or transaction not committing |
| `slow-queries` top entry called frequently | Missing index or bad plan | Check `index-usage` for that table; consider migration |
| `bloat` high on actively-written table | Vacuum not keeping up | Schedule vacuum tighter or rewrite query pattern |
| `index-usage` shows unused indexes | Wasted write cost | Drop unused indexes (after confirming they're truly unused) |
| `locks` with blocker/blocked pairs | Long transactions or deadlocks | Kill blocking PID after investigation, fix the lock-holding query |
| `cache-hit` < 0.99 | Working set exceeds RAM | Tune queries to reduce buffer churn, or scale |

## Boundaries

- **Current snapshot, not history.** `pg_stat_*` resets on Postgres restart; numbers are cumulative since last reset, not a time series.
- **Doesn't show query text inline.** `slow-queries` shows hashed query templates — get the actual SQL from [logs](logs.md) (`postgres.logs`) or `pg_stat_statements.query` directly via `db query`.
- **Doesn't evaluate RLS.** For "which policy is making this query slow," use [policies](policies.md).

## Example

User reports: "this one query is slow — `SELECT * FROM orders WHERE user_id = ... ORDER BY created_at DESC`".

```bash
# 1. Confirm it's in the slow-query log and check index usage on the table
npx @insforge/cli diagnose db --check slow-queries,index-usage

# 2. Verify no lock contention from a concurrent writer
npx @insforge/cli diagnose db --check locks

# 3. Cross-reference postgres.logs for the actual query plan / errors
npx @insforge/cli logs postgres.logs --limit 100
```

## Frequently paired with

- [logs](logs.md) — `postgres.logs` has the query text, plans, and error context behind `slow-queries`/`locks` aggregates
- [policies](policies.md) — when slow queries are RLS-gated, the policy may be adding hidden joins
- [metrics](metrics.md) — DB pressure usually shows as EC2 CPU/memory pressure; cross-reference timestamps
- [advisor](advisor.md) — performance category often pre-flags the same issues `slow-queries` / `index-usage` would surface
references/deploy-state.md
# Deploy state

History and per-deploy metadata for both frontend deployments (Vercel) and edge function deploys. The primary primitive for debugging **what happened during a deploy** — separate from runtime logs because deploy failures often never leave a runtime trace.

## Commands

```bash
# Frontend (Vercel)
npx @insforge/cli deployments list
npx @insforge/cli deployments status <id> [--sync] [--json]

# Edge function deploys
npx @insforge/cli logs function-deploy.logs [--limit <n>]
npx @insforge/cli functions list
```

## Frontend deployments

`deployments list` returns recent deploys with status. `deployments status <id> --json` returns per-deploy metadata:

| Field | Meaning |
|-------|---------|
| `status` | `pending` / `building` / `ready` / `error` / `canceled` |
| `metadata.target` | Vercel deploy target (production / preview) |
| `metadata.fileCount` | Number of files uploaded |
| `metadata.projectId` | Vercel project ID |
| `metadata.startedAt` | Build start time |
| `metadata.envVarKeys` | Env var keys baked into the build (values redacted) |
| `metadata.webhookEventType` | e.g., `deployment.succeeded`, `deployment.error` |

`--sync` re-fetches from Vercel (use when the local cached status looks stale).

## Edge function deploys

`function-deploy.logs` captures backend deploy events (compile errors, push failures, registration errors). `functions list` confirms the final state — if the function isn't there or `status != active`, the deploy didn't fully take.

## How to read

For "frontend deploy failed":

1. `deployments list` — find the failing deploy id
2. `deployments status <id> --json` — read `status` and the `metadata` block; `webhookEventType` usually names the failure stage
3. Reproduce locally: `npm run build` — server deploys often surface the same error that local build would (faster to debug locally)
4. Verify `envVarKeys` matches what the app needs at runtime

For "function deploy failed":

1. `npx @insforge/cli logs function-deploy.logs --limit 50` — find the build/push error
2. `npx @insforge/cli functions list` — confirm the function did or didn't make it into the active list
3. Re-run `npx @insforge/cli functions deploy <slug>` if needed and capture stdout for the explicit error

## Boundaries

- **Doesn't surface Vercel build logs inline.** Detailed Vercel build output lives in the Vercel dashboard or via `vercel logs`; this primitive surfaces the deploy *event metadata*.
- **Doesn't include compute service (Fly) deploy errors.** For compute services use `npx @insforge/cli compute events <id>` (machine lifecycle); container stdout/stderr is not yet exposed.
- **Distinct from runtime errors.** A function with a successful deploy can still error at invoke time — that's [logs](logs.md) `function.logs`, not this primitive.

## Example

User reports: "I ran `deployments deploy` and got an error."

```bash
# 1. Get the most recent deploy id and status
npx @insforge/cli deployments list

# 2. Full metadata on the failed one
npx @insforge/cli deployments status <id> --json

# 3. Reproduce locally to see the actual build error
npm run build
```

User reports: "`functions deploy my-handler` failed."

```bash
# 1. Re-run to capture the explicit error
npx @insforge/cli functions deploy my-handler

# 2. Backend-side deploy log
npx @insforge/cli logs function-deploy.logs --limit 50

# 3. Confirm the function isn't half-deployed
npx @insforge/cli functions list
```

## Frequently paired with

- [logs](logs.md) — `function-deploy.logs` is the source; `function.logs` is separate (runtime, not deploy)
- [metadata](metadata.md) — verify the function ended up `active` after deploy
references/error-objects.md
# Error objects

The error envelope returned by SDK calls and HTTP responses. The **entry-point primitive** — almost every reactive debug starts by reading the error and routing to the right log source / primitive.

This primitive isn't a command; it's the protocol for reading what the client got back, plus the routing tables that turn an error into "look here next."

## SDK error envelope

```ts
const { data, error } = await client.database.from('posts').select('*')
// error: { code: string, message: string, details?: string }
```

Read all three fields. `code` routes to the right log source; `message` and `details` provide context.

## Error code → log source

| Code prefix | Source | Look in |
|-------------|--------|---------|
| `PGRST*` (PostgREST errors: `PGRST204`, `PGRST301`, etc.) | PostgREST API layer | [logs](logs.md) `postgREST.logs` |
| SQL state codes (`23505` unique violation, `42P01` undefined table, etc.) | Postgres | [logs](logs.md) `postgres.logs` |
| `AUTH_*` / OAuth errors | Backend auth | [logs](logs.md) `insforge.logs` |
| Generic 500 / no code | Server error in backend | [logs](logs.md) — start with `diagnose logs` aggregate, then drill |

## HTTP status routing

| Status | What it means | Where to look |
|--------|---------------|---------------|
| **400** | Request payload/params malformed | [logs](logs.md) `postgREST.logs` for validation error |
| **401** | Auth token missing / invalid / expired | [logs](logs.md) `insforge.logs` + [metadata](metadata.md) auth config |
| **403** | RLS policy or permission denied | [logs](logs.md) `postgREST.logs` + [policies](policies.md) + [metadata](metadata.md) |
| **404** | Endpoint or resource doesn't exist | [metadata](metadata.md) — verify the table/function/bucket exists |
| **429** | Rate limit hit | **No logs** — see 429 note below |
| **500** | Server-side error | [logs](logs.md) `diagnose logs` (aggregate) → drill into specific source |
| **502 / 503 / 504** | Gateway / upstream timeout | Route by URL subsystem — see 5xx gateway note |

## 429 — special case

429 responses are **not logged** in any source and the backend does **not return** `Retry-After` or `X-RateLimit-*` headers. Checking logs is useless.

What to do:

1. Read the client code that issued the request. Look for: loops without throttling, missing debounce, retry-on-failure without exponential backoff, parallel calls that could be batched.
2. Check [metrics](metrics.md) for overall backend load context — is the system being slammed?
3. **The fix is always client-side**: reduce frequency, add backoff/debounce, batch operations.

## 5xx gateway timeout (502 / 503 / 504) — route by URL subsystem

The status alone doesn't tell you which subsystem timed out. Route by the URL path:

| URL pattern | Subsystem | Look in |
|-------------|-----------|---------|
| `/api/database/records/...` or `/api/database/...` | PostgREST → Postgres | [logs](logs.md) `postgREST.logs` then `postgres.logs`; [db-health](db-health.md) `locks`, `slow-queries` |
| `/functions/<slug>` | Edge function | [logs](logs.md) `function.logs`; check function isn't crash-looping |
| `/api/auth/...` | Auth backend | [logs](logs.md) `insforge.logs` |
| `/api/storage/...` | Storage | [logs](logs.md) `insforge.logs` |
| Any path during a system-wide spike | EC2 saturation | [metrics](metrics.md) `--range 1h` |

## Example

User pastes: `POST /api/database/records/posts returned { code: "PGRST204", message: "Column not found" }`.

1. Read code: `PGRST*` → PostgREST source. `PGRST204` specifically means **column not found** — a `select` / `columns` / `order` / filter parameter references a column that doesn't exist on the table.
2. Read `message` / `details` to identify the offending column name. Verify the table schema with `npx @insforge/cli db tables` or `npx @insforge/cli metadata --json`.
3. Drop into `postgREST.logs` for the full error context if `details` is empty:

```bash
npx @insforge/cli logs postgREST.logs --limit 50
```

## Frequently paired with

- [logs](logs.md) — every code routes to a log source for the actual error context
- [policies](policies.md) — 403 always needs policy inspection
- [metadata](metadata.md) — 404 / config-related codes need metadata to confirm the configured state
- [ai-assisted](ai-assisted.md) — when the code is unfamiliar or the error spans subsystems, hand it to `diagnose --ai`
references/logs.md
# Logs

Time-stream of events emitted by each backend service. The primary primitive for finding **when** something failed and **what the backend was doing at that moment**.

## Command

```bash
npx @insforge/cli logs <source> [--limit <n>]
```

Default limit: 20. Source names are **case-insensitive** (`postgrest.logs` works the same as `postgREST.logs`).

## Sources

| Source | What it contains | Reach for when |
|--------|------------------|----------------|
| `insforge.logs` | Main backend (auth, API gateway, realtime, edge function dispatcher, deploy controller) | Auth/OAuth errors, realtime WS errors, generic 5xx, signup/login failures |
| `postgREST.logs` | PostgREST API layer (REST/CRUD over the database) | 400 (payload), 403 (RLS denied), PGRST* error codes |
| `postgres.logs` | PostgreSQL itself | SQL errors, query warnings, slow query log, deadlock, constraint violations |
| `function.logs` | Edge function **execution** (per-invocation runtime) | Function threw / timed out / unhandled rejection |
| `function-deploy.logs` | Edge function **deploy** (build + push) | `functions deploy` failed; function isn't in `functions list` |

## Cross-source aggregate

```bash
npx @insforge/cli diagnose logs [--source <name>] [--limit <n>]
```

Aggregates **error-level only** rows across all sources. Use first when you don't know which source the error lives in.

## How to read

Each line has timestamp + source + level + message. When chasing a known-time symptom:

1. Get the approximate timestamp from the user (when did the request fail?)
2. Increase `--limit` until the window covers it (start 50, bump to 200 if needed)
3. Look for the level (`ERROR` / `WARN`) and message — the message usually names the failing component

For request-correlated symptoms (single failing URL), look for the request line in `postgREST.logs` (REST calls) or `insforge.logs` (auth/realtime/function dispatch) — both include the URL path.

## Boundaries

- **Logs are streamed, not retained forever.** If the user reports something from days ago, you may not find it. State this explicitly instead of guessing.
- **429 responses are NOT logged.** Rate limit hits don't appear in any source — confirm via [error-objects](error-objects.md) status code and check [metrics](metrics.md) for backend load context.
- **`diagnose logs` filters to errors only.** For warnings or info-level activity, query the specific source directly.

## Example

User reports: `POST /api/database/records/posts returned 500 around 14:32`.

```bash
# Aggregate first to see if the error surfaces anywhere
npx @insforge/cli diagnose logs --limit 100

# If it's a CRUD path, postgREST is the likely source
npx @insforge/cli logs postgREST.logs --limit 100

# If postgREST log shows "SQL error", drop into postgres
npx @insforge/cli logs postgres.logs --limit 100
```

## Frequently paired with

- [error-objects](error-objects.md) — start there to pick the right source from the error code/HTTP status
- [db-health](db-health.md) — when postgres.logs shows slow/locked queries, confirm with `pg_stat_*`
- [policies](policies.md) — when postgREST.logs shows RLS denial, inspect which policy fired
- [metadata](metadata.md) — when logs show auth/function/channel errors, verify the configured state
references/metadata.md
# Metadata

Declarative dump of the backend's configured state: auth provider config, database tables, storage buckets, edge functions, AI models, realtime channels. The primary primitive for **what the project is set up to do** — used to confirm configuration matches expectations, find misconfiguration, and detect drift.

## Command

```bash
npx @insforge/cli metadata [--json]
```

`--json` for structured output (preferred when extracting fields for follow-up commands).

## What you see

| Section | Contains |
|---------|----------|
| **Auth** | Configured providers (email/password, OAuth providers, third-party JWT), JWT config, allowed redirect URLs |
| **Database** | Tables with schema (columns, types), indexes, triggers |
| **Storage** | Buckets with `public` flag and size limits |
| **Functions** | Edge functions with slug, `status` (`active` / `inactive`), runtime |
| **AI** | Configured models, OpenRouter key presence |
| **Realtime** | Channel patterns and enabled flag |

## How to read

For "is this thing configured the way I think?":

| Symptom | Section to check | What to look for |
|---------|------------------|------------------|
| OAuth callback errors | Auth | Provider enabled? Redirect URLs match the callback in the request? |
| 401 / token-expired everywhere | Auth | JWT secret rotation, third-party provider integration mismatch |
| 404 on `/api/database/records/<name>` | Database | Table exists in the dump? Spelling? |
| Storage upload silently public | Storage | Bucket `public: true` when it should be `false` |
| Edge function returns 404 | Functions | Function in list with `status: "active"`? |
| `functions deploy` succeeded but invoke fails | Functions | Function `status` — may be inactive |
| Realtime channel "doesn't exist" | Realtime | Channel pattern matches what client subscribes to? `enabled: true`? |

## Boundaries

- **Configuration state, not runtime state.** Tells you what's *declared*, not what's *currently broken*. A function with `status: active` may still be crashing on every invocation — pair with [logs](logs.md) (`function.logs`).
- **Doesn't show RLS policies.** For RLS use [policies](policies.md); metadata only confirms the table exists.
- **Snapshot at query time.** Just-applied migrations or deploys may not yet be reflected — wait a moment and re-query if you suspect staleness.

## Example

User reports: "OAuth login with Google redirects but then errors out."

```bash
# 1. Pull current auth config
npx @insforge/cli metadata --json

# 2. In the auth section, confirm:
#    - google provider enabled: true
#    - redirect URLs include the exact callback the app uses
#    (e.g., https://myapp.com/auth/callback — protocol + host + path must match)

# 3. If config looks right, check insforge.logs for the OAuth error
npx @insforge/cli logs insforge.logs --limit 50
```

## Frequently paired with

- [logs](logs.md) — metadata says "configured" but logs say "actually broken"; pair to distinguish config drift vs runtime failure
- [policies](policies.md) — metadata confirms the table; policies show the RLS gating it
- [advisor](advisor.md) — security/health issues often name a configured object (bucket, secret, function) for inspection
references/metrics.md
# Metrics

EC2 instance time-series telemetry: CPU, memory, disk, network. The primary primitive for confirming **system-wide resource pressure** vs an isolated request issue.

## Command

```bash
npx @insforge/cli diagnose metrics [--range 1h|6h|24h|7d] [--metrics <list>]
```

Default range: `1h`.

## What you see

| Metric family | Indicates |
|---------------|-----------|
| **CPU** | Compute saturation (sustained >80% = trouble; spike & recover = normal) |
| **Memory** | Memory pressure (rising over time → leak; near limit + OOM kills → resize) |
| **Disk** | Storage fill rate, IO saturation (read/write throughput, queue depth) |
| **Network** | Inbound/outbound bandwidth, packet rate (sudden spike = traffic surge or attack) |

## Range selection

| Symptom | Range | Why |
|---------|-------|-----|
| Active incident ("everything is slow right now") | `1h` | High-resolution to catch the spike |
| "It was slow ~6 hours ago, what happened?" | `6h` | Cover the window with reasonable resolution |
| "Has performance degraded this week?" | `24h` or `7d` | Trend analysis, not point-in-time |
| Pre-launch baseline | `7d` | Establish normal range before traffic |

## How to read

1. **Start from baseline**: what does the normal range look like for this metric? Always look at the trend, not a single point.
2. **Correlate to events**: spike at a specific timestamp → cross-reference [logs](logs.md) for what was happening then.
3. **Distinguish saturation vs spike**:
   - Sustained high = saturation → scale up or fix the load source
   - Brief spike + recovery = normal burst → not actionable on its own

## Boundaries

- **Instance-level only.** Metrics show the EC2 box's resource use, not per-request latency or per-query cost. For request-specific perf, combine with [logs](logs.md) (`postgres.logs` for slow queries) and [db-health](db-health.md).
- **Doesn't explain causes.** Metrics show *symptoms* (CPU high), not *causes* (which query, which function). Pair with [logs](logs.md) or [db-health](db-health.md) for root cause.
- **Edge function execution is separate.** Functions run in their own runtime; their resource use isn't in EC2 metrics.

## Example

User reports: "API has been slow for the last 2 hours."

```bash
# 1. Check resource pressure over the right window
npx @insforge/cli diagnose metrics --range 6h

# 2. If CPU/memory spiked at a timestamp, line it up with errors
npx @insforge/cli diagnose logs --limit 200

# 3. If DB is the bottleneck (Postgres-heavy CPU patterns)
npx @insforge/cli diagnose db
```

## Frequently paired with

- [db-health](db-health.md) — DB is the most common bottleneck behind CPU/memory pressure
- [logs](logs.md) — correlate metric spikes to log events at the same timestamp
- [advisor](advisor.md) — `--severity critical` may already flag the underlying cause (e.g., missing index)
references/policies.md
# Policies

Active RLS (Row-Level Security) rules pulled from Postgres's `pg_policies` system view. The primary primitive for **what RLS is currently allowing or denying** — distinct from "what config we *meant* to deploy" (that lives in [metadata](metadata.md) / migration history).

## Command

```bash
npx @insforge/cli db policies
```

Returns every policy: table, schema, policy name, command (`SELECT` / `INSERT` / `UPDATE` / `DELETE` / `ALL`), target role, `USING` expression, `WITH CHECK` expression.

Only specific InsForge-managed tables allow developer RLS changes. Check the relevant module skill or CLI reference before writing policy SQL for a managed table. If that table is listed as allowing RLS changes, normal RLS operations are allowed and should go in migrations.

## Policy anatomy

| Field | Meaning |
|-------|---------|
| `tablename` | Which table the policy applies to |
| `cmd` | Which operation it gates (read/write/all) |
| `roles` | Which DB roles the policy applies to (typically `authenticated`, `anon`, `public`) |
| `qual` (USING) | Filter expression: **which existing rows the role can see/modify**. Applied to `SELECT`, `UPDATE`, `DELETE`. |
| `with_check` | Validation expression: **which new/modified rows are allowed**. Applied to `INSERT`, `UPDATE`. |
| `permissive` / `restrictive` | Permissive policies OR together; restrictive AND. Most InsForge projects use permissive. |

## How to read

For "why was this request denied?":

1. Identify the table from the request URL (`/api/database/records/<table>`).
2. List policies for that table — note `cmd` and `roles`.
3. Walk the `USING` / `WITH CHECK` expressions against the actual request:
   - **No policy for that role+cmd combo** → denied by default. Need to add a policy.
   - **`USING` evaluates false for this row** → row is invisible / not modifiable. Confirm the helper (e.g., `auth.uid()` returns the expected user_id).
   - **`WITH CHECK` evaluates false on insert** → the new row's columns violate the policy. The insert payload is wrong, or the policy is too strict.

## Common RLS bug shapes

| Symptom | Likely cause |
|---------|-------------|
| All authenticated users see all rows (no isolation) | Policy is `USING (true)` — too permissive; restrict by `user_id` column |
| Authenticated user gets empty result on own data | Wrong helper function (`auth.uid()` returns UUID; if `user_id` column is TEXT from third-party auth, use `requesting_user_id()` instead) |
| Insert fails for owner with "new row violates RLS policy" | Missing `WITH CHECK` matching the `USING`, or `WITH CHECK` references columns not in payload |
| Third-party auth (Clerk/Auth0/etc.) users get blanket deny | Wrong helper (`auth.uid()` expects InsForge-issued JWT; third-party providers need `requesting_user_id()` with the right claim extraction) |
| Anonymous user can read sensitive table | RLS not enabled on the table (forgot `ALTER TABLE ... ENABLE ROW LEVEL SECURITY`), or a policy applies to `public` / `anon` role without filter, or the connection is using a role with `BYPASSRLS` |

## Boundaries

- **Lists active policies, doesn't simulate.** Doesn't tell you "this specific request would be allowed" — combine with [logs](logs.md) (`postgREST.logs`) to see the actual denial event.
- **Doesn't include the helper function bodies.** `auth.uid()` / `requesting_user_id()` are SQL functions; inspect via `db query` if you need to verify they return what you expect.
- **Only listed managed tables allow RLS changes.** Check the relevant module skill or CLI reference before changing RLS on a managed table. If the table is listed, put normal RLS operations in [migrations](../../insforge-cli/references/database/migrations.md); keep normal schema changes in `public`.

## Example

User reports: "logged-in user gets 403 trying to `GET /api/database/records/posts`".

```bash
# 1. See the denial event
npx @insforge/cli logs postgREST.logs --limit 50

# 2. List policies on the posts table
npx @insforge/cli db policies

# 3. If the project uses third-party auth, verify the helper
npx @insforge/cli db query "SELECT requesting_user_id()"

# 4. Confirm the user's JWT contains the expected claim (auth config in metadata)
npx @insforge/cli metadata --json
```

## Frequently paired with

- [logs](logs.md) — `postgREST.logs` shows the actual RLS denial events; pair with policies to identify which rule fired
- [metadata](metadata.md) — auth config determines which claim feeds `auth.uid()` / `requesting_user_id()`
- [advisor](advisor.md) — security category often flags missing/overly-permissive RLS policies
    insforge-debug | Prompt Minder