{"openapi":"3.1.0","info":{"title":"Nanobase Console","version":"0.2.0","description":"Server-rendered dashboard for nanobase developers.\nManages projects, API keys, nanosnap subscriptions, billing, and usage analytics.\n\n## Authentication\n- **Cookie-based JWT**: Set by ppn-hub OAuth login.\n  Dashboard and billing routes require authentication.\n- **Admin role**: Admin routes require role = 'admin' in JWT.\n\n## Response Format\n- Dashboard routes return server-rendered HTML.\n- Billing subscribe and webhook routes return JSON.\n"},"servers":[{"url":"https://www.nanobase.cc","description":"Production"}],"tags":[{"name":"Public","description":"Health check"},{"name":"Dashboard","description":"Developer dashboard (HTML responses, auth required)"},{"name":"Projects","description":"Project and API key management (auth required)"},{"name":"Pieces","description":"Nanosnap game API pieces catalog (auth required)"},{"name":"Billing","description":"Subscription and payment management"},{"name":"Usage","description":"Usage analytics (auth required)"},{"name":"Settings","description":"User profile settings (auth required)"},{"name":"Admin","description":"Platform administration (admin role required)"}],"paths":{"/health":{"get":{"summary":"Health check","tags":["Public"],"responses":{"200":{"description":"Service status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"service":{"type":"string","example":"nanobase-console"},"version":{"type":"string","example":"0.2.0"}}}}}}}}},"/":{"get":{"summary":"Root redirect","tags":["Public"],"responses":{"302":{"description":"Redirects to /dashboard"}}}},"/dashboard":{"get":{"summary":"Overview dashboard","description":"Shows project count, API key count, nanosnap subscriptions, and current plan.","tags":["Dashboard"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML dashboard page"},"302":{"description":"Redirect to login if not authenticated"}}}},"/dashboard/projects":{"get":{"summary":"List projects","tags":["Projects"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML project list page"}}},"post":{"summary":"Create a project","tags":["Projects"],"security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":100},"description":{"type":"string"}}}}}},"responses":{"302":{"description":"Redirect to project list"}}}},"/dashboard/projects/{id}/delete":{"post":{"summary":"Delete a project","tags":["Projects"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to project list"}}}},"/dashboard/projects/{id}/keys":{"get":{"summary":"List API keys for a project","tags":["Projects"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"HTML API keys page"}}},"post":{"summary":"Create an API key","tags":["Projects"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"key_name":{"type":"string"}}}}}},"responses":{"302":{"description":"Redirect to keys page (new key shown via flash cookie)"}}}},"/dashboard/projects/{id}/keys/{kid}/delete":{"post":{"summary":"Delete an API key","tags":["Projects"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"kid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to keys page"}}}},"/dashboard/pieces":{"get":{"summary":"Nanosnap pieces catalog","description":"Lists available and coming-soon game API pieces with subscription status.","tags":["Pieces"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML pieces catalog page"}}}},"/dashboard/pieces/{pieceId}":{"get":{"summary":"Piece details and pricing","tags":["Pieces"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"pieceId","in":"path","required":true,"schema":{"type":"string","enum":["auth","room","inventory","world-counter"]}}],"responses":{"200":{"description":"HTML piece detail page with tier pricing"}}}},"/billing/nanobase":{"get":{"summary":"Nanobase plan comparison","tags":["Billing"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML billing page with plan comparison"}}}},"/billing/nanobase/subscribe":{"post":{"summary":"Change nanobase plan","tags":["Billing"],"security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["tier_id"],"properties":{"tier_id":{"type":"string","enum":["free","pro","business"]}}}}}},"responses":{"302":{"description":"Redirect to billing page"}}}},"/billing/nanosnap":{"get":{"summary":"Nanosnap subscription list","tags":["Billing"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML subscription list page"}}}},"/billing/nanosnap/subscribe":{"post":{"summary":"Subscribe to a nanosnap piece","tags":["Billing"],"security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pieceId","tierId"],"properties":{"pieceId":{"type":"string"},"tierId":{"type":"string"}}}}}},"responses":{"200":{"description":"Free tier: returns subscription with API key.\nPaid tier: returns PayPal approval URL.\n","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"subscription":{"type":"object"},"approvalUrl":{"type":"string","format":"uri"}}}}}}}}},"/billing/nanosnap/callback/success":{"get":{"summary":"PayPal success callback","tags":["Billing"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"piece","in":"query","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to nanosnap billing page"}}}},"/billing/nanosnap/callback/cancel":{"get":{"summary":"PayPal cancel callback","tags":["Billing"],"security":[{"cookieAuth":[]}],"responses":{"302":{"description":"Redirect to nanosnap billing page"}}}},"/billing/nanosnap/cancel":{"post":{"summary":"Cancel nanosnap subscription","tags":["Billing"],"security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["piece_id"],"properties":{"piece_id":{"type":"string"}}}}}},"responses":{"302":{"description":"Redirect to nanosnap billing page"}}}},"/billing/webhook/paypal":{"post":{"summary":"PayPal webhook handler","description":"Handles subscription lifecycle events. Verified via PayPal signature.","tags":["Billing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Webhook processed"}}}},"/dashboard/usage":{"get":{"summary":"Usage analytics dashboard","description":"Shows nanobase and nanosnap usage with daily breakdown (last 30 days).","tags":["Usage"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML usage analytics page"}}}},"/dashboard/settings":{"get":{"summary":"User profile settings","tags":["Settings"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"HTML settings page"}}},"post":{"summary":"Update display name","tags":["Settings"],"security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"302":{"description":"Redirect to settings page"}}}},"/admin":{"get":{"summary":"User management list","tags":["Admin"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Search by email or name"},{"name":"role","in":"query","schema":{"type":"string","enum":["user","admin","disabled"]}},{"name":"page","in":"query","schema":{"type":"integer","default":1}}],"responses":{"200":{"description":"HTML admin user list (paginated, 20/page)"},"403":{"description":"Not an admin"}}}},"/admin/users/{id}":{"get":{"summary":"User details with projects and subscriptions","tags":["Admin"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"HTML user detail page"}}}},"/admin/users/{id}/role":{"post":{"summary":"Change user role","tags":["Admin"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["user","admin","disabled"]}}}}}},"responses":{"302":{"description":"Redirect to user detail page"},"400":{"description":"Cannot demote self"}}}},"/auth/callback":{"get":{"summary":"Get auth/callback","description":"Auto-generated stub. Schema/responses/parameters TBD.\nVerify path matches the actual mounted URL (sub-router prefix may be missing).\n","x-stub":true,"responses":{"200":{"description":"Successful response (schema TBD)"}}}},"/console":{"get":{"summary":"Get console","description":"Auto-generated stub. Schema/responses/parameters TBD.\nVerify path matches the actual mounted URL (sub-router prefix may be missing).\n","x-stub":true,"responses":{"200":{"description":"Successful response (schema TBD)"}}}},"/list":{"get":{"summary":"Get list","description":"Auto-generated stub. Schema/responses/parameters TBD.\nVerify path matches the actual mounted URL (sub-router prefix may be missing).\n","x-stub":true,"responses":{"200":{"description":"Successful response (schema TBD)"}}}}},"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"auth","description":"JWT cookie set by ppn-hub OAuth login"}}}}