Skip to content

Commit b14f592

Browse files
docs(openapi): widen API title/description to full bundle + AI-agent/MCP framing
The OpenAPI info block — the first thing an AI coding agent fetches to decide what the API does — only mentioned "databases, caches, and queues" and a generic "InstaNode API" title. It omitted pgvector/vector, object storage, webhooks, and the headline deployment wedge, plus any AI-agent / MCP / free-tier / single-HTTP-call framing. Framing-only change (no path/parameter/schema contract edits): - info.title → "instanode.dev — zero-friction dev infrastructure for AI agents" - info.description widened to lead with the keywords agents match: Postgres, pgvector/vector, Redis, MongoDB, NATS queues, S3 storage, webhooks, AND app deployment — each a single HTTP call, no account/Docker/setup; free anon tier; also MCP server + SDKs + CLI. Accurate to the live router surface. - Added a top-level tags[] block describing each service family (database/vector/cache/nosql/queue/storage/webhook/deploy/auth). - Regenerated openapi.snapshot.json (description is content, so the canonical snapshot changed; drift check passes). No paths, parameters, or schemas added/removed/renamed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9aba000 commit b14f592

2 files changed

Lines changed: 53 additions & 4 deletions

File tree

internal/handlers/openapi.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,21 @@ func OpenAPISpecProduction() string {
183183
const openAPISpec = `{
184184
"openapi": "3.1.0",
185185
"info": {
186-
"title": "InstaNode API",
186+
"title": "instanode.dev — zero-friction dev infrastructure for AI agents",
187187
"version": "1.0.0",
188-
"description": "Zero-friction developer infrastructure. Provision real databases, caches, and queues with a single HTTP call — no account, no Docker, no setup.\n\n## Idempotency\n\nEvery POST endpoint that creates a resource is idempotent. Two layered protections cover every retry pattern:\n\n1. Explicit Idempotency-Key header (Stripe-shape, 24h TTL). Pass the same opaque key on each retry of a logical operation and the server replays the first response verbatim. Reusing a key with a different body returns 409.\n2. Body-fingerprint fallback (120s TTL). When the header is absent, the server synthesises a key from sha256(scope, route, canonical-body) and dedups identical retries inside a 120s window. Absorbs double-clicks, mobile double-taps, agent retries on transient 5xx, and reverse-proxy retries on network blips. Use the explicit header for true exactly-once across longer windows.\n\nEvery response from a create endpoint carries:\n- X-Idempotency-Source: explicit | fingerprint | miss — which dedup path matched (explicit = caller passed an Idempotency-Key; fingerprint = the body-fingerprint cache replayed; miss = handler ran fresh).\n- X-Idempotent-Replay: true — present only when the response was served from the cache (either path).\n\n## Rate limit (applies to every route)\n\nA global per-IP rate limit (100 req/min) is applied to EVERY documented endpoint by the router middleware. Exceeding it returns 429 with the standard ErrorResponse envelope (error=rate_limited), a Retry-After HTTP header, and retry_after_seconds in the JSON body. The per-route response maps below may omit 429 for brevity; the canonical 429 shape is documented under components.responses.TooManyRequests and applies to every path. T19 P1-1 (BugHunt 2026-05-20).\n\n## Payload size (applies to every route)\n\nFiber's global BodyLimit is set to 50 MiB — only /deploy/new and /stacks/new (multipart tarballs) and /webhooks/github/* (push payloads) approach that cap; JSON endpoints are bounded to sub-KB bodies by the per-handler shape. Oversized requests return 413 payload_too_large with the standard JSON ErrorResponse envelope (NOT the upstream nginx HTML 502 the older shape returned — T19 P1-2). The canonical 413 shape is documented under components.responses.PayloadTooLarge.\n\n## Security headers (applies to every response)\n\nEvery response from EVERY route — including liveness/readiness probes, OpenAPI document fetch, 4xx error envelopes, 5xx error envelopes, and 404/405 Fiber-default responses — carries the following defense-in-depth response headers, set by the SecurityHeaders middleware ahead of RequestID in the router middleware chain (task #311 wave-3 chaos-verify redo):\n\n- Strict-Transport-Security: max-age=63072000; includeSubDomains — production only (omitted on ENVIRONMENT=development so local http://localhost:8080 doesn't poison the host's HSTS cache). 2-year max-age, includeSubDomains for *.api.instanode.dev.\n- X-Content-Type-Options: nosniff — disables MIME sniffing.\n- X-Frame-Options: SAMEORIGIN — clickjacking defense.\n- Referrer-Policy: strict-origin-when-cross-origin — prevents URL-token leakage across origin downgrades.\n- Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=() — denies powerful browser APIs.\n- Cross-Origin-Resource-Policy: same-origin — blocks no-cors cross-origin fetches.\n\nThese headers are not enumerated in each per-route responses block to keep the spec readable; they apply globally. Coverage test: TestSecurityHeaders_AllEndpoints_AllHeaders_Prod (internal/handlers/security_headers_test.go) iterates 5 representative endpoints (healthz, readyz, openapi.json, db/new, claim) and asserts all 6 headers land on every response."
188+
"description": "Zero-friction developer infrastructure built for AI coding agents (Claude Code, Cursor, MCP tool-use) and humans alike. Provision real Postgres + pgvector + Redis + MongoDB + NATS JetStream queues + S3-compatible object storage + webhook receivers — AND deploy your app on top of them — each with a single HTTP call. No account, no Docker, no setup. A free anonymous tier (24h TTL) lets an agent claim infrastructure the moment it hits a limit, with no signup. Also available as an MCP server, language SDKs, and a CLI for agent tool-use. The unit of value is the whole bundle: everything an agent needs to ship a working app, claimed and provisioned in one flow. Keywords: AI agent infrastructure, MCP, Postgres, pgvector / vector database, Redis cache, MongoDB, NATS queue, S3 object storage, webhooks, app deployment, free tier, single HTTP call, no signup.\n\n## Idempotency\n\nEvery POST endpoint that creates a resource is idempotent. Two layered protections cover every retry pattern:\n\n1. Explicit Idempotency-Key header (Stripe-shape, 24h TTL). Pass the same opaque key on each retry of a logical operation and the server replays the first response verbatim. Reusing a key with a different body returns 409.\n2. Body-fingerprint fallback (120s TTL). When the header is absent, the server synthesises a key from sha256(scope, route, canonical-body) and dedups identical retries inside a 120s window. Absorbs double-clicks, mobile double-taps, agent retries on transient 5xx, and reverse-proxy retries on network blips. Use the explicit header for true exactly-once across longer windows.\n\nEvery response from a create endpoint carries:\n- X-Idempotency-Source: explicit | fingerprint | miss — which dedup path matched (explicit = caller passed an Idempotency-Key; fingerprint = the body-fingerprint cache replayed; miss = handler ran fresh).\n- X-Idempotent-Replay: true — present only when the response was served from the cache (either path).\n\n## Rate limit (applies to every route)\n\nA global per-IP rate limit (100 req/min) is applied to EVERY documented endpoint by the router middleware. Exceeding it returns 429 with the standard ErrorResponse envelope (error=rate_limited), a Retry-After HTTP header, and retry_after_seconds in the JSON body. The per-route response maps below may omit 429 for brevity; the canonical 429 shape is documented under components.responses.TooManyRequests and applies to every path. T19 P1-1 (BugHunt 2026-05-20).\n\n## Payload size (applies to every route)\n\nFiber's global BodyLimit is set to 50 MiB — only /deploy/new and /stacks/new (multipart tarballs) and /webhooks/github/* (push payloads) approach that cap; JSON endpoints are bounded to sub-KB bodies by the per-handler shape. Oversized requests return 413 payload_too_large with the standard JSON ErrorResponse envelope (NOT the upstream nginx HTML 502 the older shape returned — T19 P1-2). The canonical 413 shape is documented under components.responses.PayloadTooLarge.\n\n## Security headers (applies to every response)\n\nEvery response from EVERY route — including liveness/readiness probes, OpenAPI document fetch, 4xx error envelopes, 5xx error envelopes, and 404/405 Fiber-default responses — carries the following defense-in-depth response headers, set by the SecurityHeaders middleware ahead of RequestID in the router middleware chain (task #311 wave-3 chaos-verify redo):\n\n- Strict-Transport-Security: max-age=63072000; includeSubDomains — production only (omitted on ENVIRONMENT=development so local http://localhost:8080 doesn't poison the host's HSTS cache). 2-year max-age, includeSubDomains for *.api.instanode.dev.\n- X-Content-Type-Options: nosniff — disables MIME sniffing.\n- X-Frame-Options: SAMEORIGIN — clickjacking defense.\n- Referrer-Policy: strict-origin-when-cross-origin — prevents URL-token leakage across origin downgrades.\n- Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=() — denies powerful browser APIs.\n- Cross-Origin-Resource-Policy: same-origin — blocks no-cors cross-origin fetches.\n\nThese headers are not enumerated in each per-route responses block to keep the spec readable; they apply globally. Coverage test: TestSecurityHeaders_AllEndpoints_AllHeaders_Prod (internal/handlers/security_headers_test.go) iterates 5 representative endpoints (healthz, readyz, openapi.json, db/new, claim) and asserts all 6 headers land on every response."
189189
},
190+
"tags": [
191+
{ "name": "database", "description": "Real Postgres connection strings via POST /db/new — encrypted at rest, per-token isolation, instant." },
192+
{ "name": "vector", "description": "pgvector-enabled Postgres via POST /vector/new — embedding stores with HNSW + IVFFlat for AI/RAG workloads." },
193+
{ "name": "cache", "description": "Real Redis connection strings via POST /cache/new — ACL namespace isolation." },
194+
{ "name": "nosql", "description": "Real MongoDB connection strings via POST /nosql/new — per-token database scoping." },
195+
{ "name": "queue", "description": "NATS JetStream URLs via POST /queue/new — per-account subject isolation." },
196+
{ "name": "storage", "description": "S3-compatible object storage via POST /storage/new plus broker-mode signed URLs via POST /storage/{token}/presign." },
197+
{ "name": "webhook", "description": "Public webhook receiver URLs via POST /webhook/new — captures any HTTP method payload." },
198+
{ "name": "deploy", "description": "The deployment wedge: ship an app via POST /deploy/new (single app) or POST /stacks/new (multi-service) — Docker build to public HTTPS URL with TLS, no Dockerfile-on-disk required." },
199+
{ "name": "auth", "description": "Magic-link, GitHub OAuth, and CLI device-flow login — used to claim a free anonymous resource bundle." }
200+
],
190201
"servers": [{ "url": "https://api.instanode.dev", "description": "Production" }],
191202
"paths": {
192203
"/livez": {

openapi.snapshot.json

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,8 +2566,8 @@
25662566
}
25672567
},
25682568
"info": {
2569-
"description": "Zero-friction developer infrastructure. Provision real databases, caches, and queues with a single HTTP call — no account, no Docker, no setup.\n\n## Idempotency\n\nEvery POST endpoint that creates a resource is idempotent. Two layered protections cover every retry pattern:\n\n1. Explicit Idempotency-Key header (Stripe-shape, 24h TTL). Pass the same opaque key on each retry of a logical operation and the server replays the first response verbatim. Reusing a key with a different body returns 409.\n2. Body-fingerprint fallback (120s TTL). When the header is absent, the server synthesises a key from sha256(scope, route, canonical-body) and dedups identical retries inside a 120s window. Absorbs double-clicks, mobile double-taps, agent retries on transient 5xx, and reverse-proxy retries on network blips. Use the explicit header for true exactly-once across longer windows.\n\nEvery response from a create endpoint carries:\n- X-Idempotency-Source: explicit | fingerprint | miss — which dedup path matched (explicit = caller passed an Idempotency-Key; fingerprint = the body-fingerprint cache replayed; miss = handler ran fresh).\n- X-Idempotent-Replay: true — present only when the response was served from the cache (either path).\n\n## Rate limit (applies to every route)\n\nA global per-IP rate limit (100 req/min) is applied to EVERY documented endpoint by the router middleware. Exceeding it returns 429 with the standard ErrorResponse envelope (error=rate_limited), a Retry-After HTTP header, and retry_after_seconds in the JSON body. The per-route response maps below may omit 429 for brevity; the canonical 429 shape is documented under components.responses.TooManyRequests and applies to every path. T19 P1-1 (BugHunt 2026-05-20).\n\n## Payload size (applies to every route)\n\nFiber's global BodyLimit is set to 50 MiB — only /deploy/new and /stacks/new (multipart tarballs) and /webhooks/github/* (push payloads) approach that cap; JSON endpoints are bounded to sub-KB bodies by the per-handler shape. Oversized requests return 413 payload_too_large with the standard JSON ErrorResponse envelope (NOT the upstream nginx HTML 502 the older shape returned — T19 P1-2). The canonical 413 shape is documented under components.responses.PayloadTooLarge.\n\n## Security headers (applies to every response)\n\nEvery response from EVERY route — including liveness/readiness probes, OpenAPI document fetch, 4xx error envelopes, 5xx error envelopes, and 404/405 Fiber-default responses — carries the following defense-in-depth response headers, set by the SecurityHeaders middleware ahead of RequestID in the router middleware chain (task #311 wave-3 chaos-verify redo):\n\n- Strict-Transport-Security: max-age=63072000; includeSubDomains — production only (omitted on ENVIRONMENT=development so local http://localhost:8080 doesn't poison the host's HSTS cache). 2-year max-age, includeSubDomains for *.api.instanode.dev.\n- X-Content-Type-Options: nosniff — disables MIME sniffing.\n- X-Frame-Options: SAMEORIGIN — clickjacking defense.\n- Referrer-Policy: strict-origin-when-cross-origin — prevents URL-token leakage across origin downgrades.\n- Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=() — denies powerful browser APIs.\n- Cross-Origin-Resource-Policy: same-origin — blocks no-cors cross-origin fetches.\n\nThese headers are not enumerated in each per-route responses block to keep the spec readable; they apply globally. Coverage test: TestSecurityHeaders_AllEndpoints_AllHeaders_Prod (internal/handlers/security_headers_test.go) iterates 5 representative endpoints (healthz, readyz, openapi.json, db/new, claim) and asserts all 6 headers land on every response.",
2570-
"title": "InstaNode API",
2569+
"description": "Zero-friction developer infrastructure built for AI coding agents (Claude Code, Cursor, MCP tool-use) and humans alike. Provision real Postgres + pgvector + Redis + MongoDB + NATS JetStream queues + S3-compatible object storage + webhook receivers — AND deploy your app on top of them — each with a single HTTP call. No account, no Docker, no setup. A free anonymous tier (24h TTL) lets an agent claim infrastructure the moment it hits a limit, with no signup. Also available as an MCP server, language SDKs, and a CLI for agent tool-use. The unit of value is the whole bundle: everything an agent needs to ship a working app, claimed and provisioned in one flow. Keywords: AI agent infrastructure, MCP, Postgres, pgvector / vector database, Redis cache, MongoDB, NATS queue, S3 object storage, webhooks, app deployment, free tier, single HTTP call, no signup.\n\n## Idempotency\n\nEvery POST endpoint that creates a resource is idempotent. Two layered protections cover every retry pattern:\n\n1. Explicit Idempotency-Key header (Stripe-shape, 24h TTL). Pass the same opaque key on each retry of a logical operation and the server replays the first response verbatim. Reusing a key with a different body returns 409.\n2. Body-fingerprint fallback (120s TTL). When the header is absent, the server synthesises a key from sha256(scope, route, canonical-body) and dedups identical retries inside a 120s window. Absorbs double-clicks, mobile double-taps, agent retries on transient 5xx, and reverse-proxy retries on network blips. Use the explicit header for true exactly-once across longer windows.\n\nEvery response from a create endpoint carries:\n- X-Idempotency-Source: explicit | fingerprint | miss — which dedup path matched (explicit = caller passed an Idempotency-Key; fingerprint = the body-fingerprint cache replayed; miss = handler ran fresh).\n- X-Idempotent-Replay: true — present only when the response was served from the cache (either path).\n\n## Rate limit (applies to every route)\n\nA global per-IP rate limit (100 req/min) is applied to EVERY documented endpoint by the router middleware. Exceeding it returns 429 with the standard ErrorResponse envelope (error=rate_limited), a Retry-After HTTP header, and retry_after_seconds in the JSON body. The per-route response maps below may omit 429 for brevity; the canonical 429 shape is documented under components.responses.TooManyRequests and applies to every path. T19 P1-1 (BugHunt 2026-05-20).\n\n## Payload size (applies to every route)\n\nFiber's global BodyLimit is set to 50 MiB — only /deploy/new and /stacks/new (multipart tarballs) and /webhooks/github/* (push payloads) approach that cap; JSON endpoints are bounded to sub-KB bodies by the per-handler shape. Oversized requests return 413 payload_too_large with the standard JSON ErrorResponse envelope (NOT the upstream nginx HTML 502 the older shape returned — T19 P1-2). The canonical 413 shape is documented under components.responses.PayloadTooLarge.\n\n## Security headers (applies to every response)\n\nEvery response from EVERY route — including liveness/readiness probes, OpenAPI document fetch, 4xx error envelopes, 5xx error envelopes, and 404/405 Fiber-default responses — carries the following defense-in-depth response headers, set by the SecurityHeaders middleware ahead of RequestID in the router middleware chain (task #311 wave-3 chaos-verify redo):\n\n- Strict-Transport-Security: max-age=63072000; includeSubDomains — production only (omitted on ENVIRONMENT=development so local http://localhost:8080 doesn't poison the host's HSTS cache). 2-year max-age, includeSubDomains for *.api.instanode.dev.\n- X-Content-Type-Options: nosniff — disables MIME sniffing.\n- X-Frame-Options: SAMEORIGIN — clickjacking defense.\n- Referrer-Policy: strict-origin-when-cross-origin — prevents URL-token leakage across origin downgrades.\n- Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=() — denies powerful browser APIs.\n- Cross-Origin-Resource-Policy: same-origin — blocks no-cors cross-origin fetches.\n\nThese headers are not enumerated in each per-route responses block to keep the spec readable; they apply globally. Coverage test: TestSecurityHeaders_AllEndpoints_AllHeaders_Prod (internal/handlers/security_headers_test.go) iterates 5 representative endpoints (healthz, readyz, openapi.json, db/new, claim) and asserts all 6 headers land on every response.",
2570+
"title": "instanode.dev — zero-friction dev infrastructure for AI agents",
25712571
"version": "1.0.0"
25722572
},
25732573
"openapi": "3.1.0",
@@ -10177,5 +10177,43 @@
1017710177
"description": "Production",
1017810178
"url": "https://api.instanode.dev"
1017910179
}
10180+
],
10181+
"tags": [
10182+
{
10183+
"description": "Real Postgres connection strings via POST /db/new — encrypted at rest, per-token isolation, instant.",
10184+
"name": "database"
10185+
},
10186+
{
10187+
"description": "pgvector-enabled Postgres via POST /vector/new — embedding stores with HNSW + IVFFlat for AI/RAG workloads.",
10188+
"name": "vector"
10189+
},
10190+
{
10191+
"description": "Real Redis connection strings via POST /cache/new — ACL namespace isolation.",
10192+
"name": "cache"
10193+
},
10194+
{
10195+
"description": "Real MongoDB connection strings via POST /nosql/new — per-token database scoping.",
10196+
"name": "nosql"
10197+
},
10198+
{
10199+
"description": "NATS JetStream URLs via POST /queue/new — per-account subject isolation.",
10200+
"name": "queue"
10201+
},
10202+
{
10203+
"description": "S3-compatible object storage via POST /storage/new plus broker-mode signed URLs via POST /storage/{token}/presign.",
10204+
"name": "storage"
10205+
},
10206+
{
10207+
"description": "Public webhook receiver URLs via POST /webhook/new — captures any HTTP method payload.",
10208+
"name": "webhook"
10209+
},
10210+
{
10211+
"description": "The deployment wedge: ship an app via POST /deploy/new (single app) or POST /stacks/new (multi-service) — Docker build to public HTTPS URL with TLS, no Dockerfile-on-disk required.",
10212+
"name": "deploy"
10213+
},
10214+
{
10215+
"description": "Magic-link, GitHub OAuth, and CLI device-flow login — used to claim a free anonymous resource bundle.",
10216+
"name": "auth"
10217+
}
1018010218
]
1018110219
}

0 commit comments

Comments
 (0)