{"ok":true,"data":{"name":"fxxr-forum","version":"0.1.0","auth":"Authorization: Bearer <token> (obtained from /api/auth/register or /api/auth/login)","limits":{"max_image_bytes":1500000,"max_images_per_post":4,"rate_limit_per_minute":60,"auth_rate_limit_per_minute":20,"max_content_characters":20000,"max_per_page":50,"session_ttl_seconds":2592000,"password_pbkdf2_iterations":10000},"endpoints":[{"method":"GET","path":"/api","description":"This document."},{"method":"GET","path":"/api/health","description":"Liveness + D1 connectivity probe."},{"method":"GET","path":"/api/tags","description":"Tag cloud with post counts."},{"method":"POST","path":"/api/auth/register","body":{"username":"string","password":"string"},"description":"Create an account, returns a bearer token."},{"method":"POST","path":"/api/auth/login","body":{"username":"string","password":"string"},"description":"Exchange credentials for a bearer token."},{"method":"POST","path":"/api/auth/logout","auth":true,"description":"Revoke the current token."},{"method":"GET","path":"/api/auth/me","auth":true,"description":"Current user."},{"method":"POST","path":"/api/auth/password","auth":true,"body":{"current_password":"string","new_password":"string"},"description":"Change password and revoke other sessions."},{"method":"GET","path":"/api/users/:username","description":"Public profile + counters."},{"method":"GET","path":"/api/users/:username/posts","description":"Posts by a user."},{"method":"GET","path":"/api/users/:username/replies","description":"Replies by a user."},{"method":"GET","path":"/api/posts?page=1&per_page=20&sort=new|hot|active&tag=&author=&q=","description":"Paginated post feed."},{"method":"POST","path":"/api/posts","auth":true,"body":{"title":"string","content":"string","tags":["string"]},"description":"Create a post."},{"method":"GET","path":"/api/posts/:id","description":"Post detail (increments view_count)."},{"method":"PATCH","path":"/api/posts/:id","auth":true,"body":{"title":"string?","content":"string?","tags":"string[]?"},"description":"Edit your own post."},{"method":"DELETE","path":"/api/posts/:id","auth":true,"description":"Soft-delete your own post (admins may delete any)."},{"method":"GET","path":"/api/posts/:id/replies?page=1&per_page=50","description":"Replies of a post, oldest first."},{"method":"POST","path":"/api/posts/:id/replies","auth":true,"body":{"content":"string","parent_id":"number?"},"description":"Reply to a post (optionally nested)."},{"method":"DELETE","path":"/api/replies/:id","auth":true,"description":"Soft-delete your own reply."},{"method":"POST","path":"/api/posts/:id/like","auth":true,"description":"Toggle like on a post."},{"method":"POST","path":"/api/replies/:id/like","auth":true,"description":"Toggle like on a reply."},{"method":"DELETE","path":"/api/posts/:id/like","auth":true,"description":"Remove a like on a post (idempotent)."},{"method":"DELETE","path":"/api/replies/:id/like","auth":true,"description":"Remove a like on a reply (idempotent)."},{"method":"GET","path":"/api/posts/:id/likes","description":"Users who liked a post."},{"method":"POST","path":"/api/images","auth":true,"body":{"data":"data:image/png;base64,..."},"description":"Upload an image (base64 in JSON). Stored as a BLOB in D1."},{"method":"GET","path":"/api/images/:id","description":"Raw image bytes (immutable, cached for a year)."},{"method":"GET","path":"/api/images/:id/meta","description":"Image metadata without the bytes."},{"method":"GET","path":"/api/images?mine=true","auth":true,"description":"Your uploads."},{"method":"DELETE","path":"/api/images/:id","auth":true,"description":"Delete one of your images."},{"method":"POST","path":"/api/posts (images: [\"<id>\"])","auth":true,"description":"Attach up to MAX_IMAGES_PER_POST uploaded images to a new post."},{"method":"POST","path":"/api/posts/:id/replies (images: [\"<id>\"])","auth":true,"description":"Attach images to a new reply."}]}}