{ "openapi": "3.0.3", "info": { "title": "Anonymous GitHub maintenance API", "version": "1.0.0", "description": "Maintain existing anonymized snapshots. Session-only token issuance is documented at /llms.txt. No GitHub proxy or file download access." }, "servers": [ { "url": "/" } ], "security": [ { "maintenanceToken": [] } ], "components": { "securitySchemes": { "maintenanceToken": { "type": "http", "scheme": "bearer" } }, "schemas": { "Error": { "type": "object", "required": [ "error" ], "properties": { "error": { "type": "string" }, "resetAt": { "type": "number", "description": "Unix timestamp in milliseconds, when available." } } }, "RepositoryStatus": { "type": "object", "required": [ "repoId", "options" ], "properties": { "repoId": { "type": "string" }, "status": { "type": "string" }, "statusMessage": { "type": "string" }, "statusDate": { "type": "string", "format": "date-time" }, "settingsSavedAt": { "type": "string", "format": "date-time" }, "source": { "type": "object", "properties": { "branch": { "type": "string" }, "commit": { "type": "string" } } }, "options": { "type": "object", "properties": { "terms": { "type": "array", "maxItems": 1000, "items": { "type": "string", "minLength": 1, "maxLength": 1000 } }, "preserveFiles": { "type": "array", "maxItems": 200, "description": "Glob patterns of files whose content is not redacted. Same rules as .gitignore: a pattern without / matches the file name in any folder; one containing / (including a leading /) matches from the repository root; !pattern excludes again and the last match wins. Replaces the whole list.", "items": { "type": "string", "maxLength": 512 } }, "update": { "type": "boolean" }, "image": { "type": "boolean" }, "pdf": { "type": "boolean" }, "notebook": { "type": "boolean" }, "link": { "type": "boolean" }, "expirationDate": { "type": "string", "format": "date-time" }, "expirationMode": { "type": "string", "enum": [ "never", "redirect", "remove" ] }, "page": { "type": "boolean" }, "pageSource": { "type": "object", "properties": { "branch": { "type": "string" }, "path": { "type": "string" } } } } } } } } }, "paths": { "/api/maintenance/repositories/{repoId}": { "parameters": [ { "name": "repoId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" }, "description": "Existing anonymized repository ID selected when issuing the token." } ], "get": { "operationId": "getRepositoryMaintenanceStatus", "summary": "Read saved status and settings without GitHub calls", "responses": { "200": { "description": "Operation succeeded; refresh or restoration may still be running.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RepositoryStatus" } } } }, "400": { "description": "Invalid settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Invalid or expired token.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "Scope or membership denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Repository not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Repository state changed or operation unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "Rate limited. Respect Retry-After.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }, "text/plain": { "schema": { "type": "string" } } } }, "503": { "description": "Service unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "patch": { "operationId": "patchRepositorySettings", "summary": "Change only supplied settings; terms replaces the entire list", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "minProperties": 1, "properties": { "terms": { "type": "array", "maxItems": 1000, "items": { "type": "string", "minLength": 1, "maxLength": 1000 } }, "preserveFiles": { "type": "array", "maxItems": 200, "description": "Glob patterns of files whose content is not redacted. Same rules as .gitignore: a pattern without / matches the file name in any folder; one containing / (including a leading /) matches from the repository root; !pattern excludes again and the last match wins. Replaces the whole list.", "items": { "type": "string", "maxLength": 512 } }, "update": { "type": "boolean" }, "image": { "type": "boolean" }, "pdf": { "type": "boolean" }, "notebook": { "type": "boolean" }, "link": { "type": "boolean" } } } } } }, "responses": { "200": { "description": "Operation succeeded; refresh or restoration may still be running.", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string" }, "repoId": { "type": "string" }, "settingsSavedAt": { "type": "string", "format": "date-time" } } } } } }, "400": { "description": "Invalid settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Invalid or expired token.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "Scope or membership denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Repository not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Repository state changed or operation unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "Rate limited. Respect Retry-After.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }, "text/plain": { "schema": { "type": "string" } } } }, "503": { "description": "Service unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/api/repo/{repoId}/refresh": { "parameters": [ { "name": "repoId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" }, "description": "Existing anonymized repository ID selected when issuing the token." } ], "post": { "operationId": "refreshRepository", "summary": "Queue refresh of the saved branch. Poll status, not refresh.", "responses": { "200": { "description": "Operation succeeded; refresh or restoration may still be running.", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string" } } } } } }, "400": { "description": "Invalid settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Invalid or expired token.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "Scope or membership denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Repository not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Repository state changed or operation unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "Rate limited. Respect Retry-After.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }, "text/plain": { "schema": { "type": "string" } } } }, "503": { "description": "Service unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "description": "One refresh per repository per minute per account, shared across tokens and IPs. HTTP 429 includes Retry-After." } }, "/api/repo/{repoId}/extend": { "parameters": [ { "name": "repoId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" }, "description": "Existing anonymized repository ID selected when issuing the token." } ], "post": { "operationId": "extendRepository", "summary": "Extend expiration by six months. Not idempotent; inspect state before retrying.", "responses": { "200": { "description": "Operation succeeded; refresh or restoration may still be running.", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string" }, "expirationDate": { "type": "string", "format": "date-time" } } } } } }, "400": { "description": "Invalid settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Invalid or expired token.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "Scope or membership denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Repository not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Repository state changed or operation unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "Rate limited. Respect Retry-After.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }, "text/plain": { "schema": { "type": "string" } } } }, "503": { "description": "Service unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } } }