diff --git a/contracts/openapi.yaml b/contracts/openapi.yaml new file mode 100644 index 0000000..8ae6e3d --- /dev/null +++ b/contracts/openapi.yaml @@ -0,0 +1,4067 @@ +# GENERATED — do not hand-edit. +# Source of truth: the FastAPI application (backend/app/main.py). +# Regenerate: python scripts/export-openapi.py +# Verify: python scripts/export-openapi.py --check +openapi: 3.1.0 +info: + title: FuzeKeys API + description: 'FuzeKeys — identity and credential vault. Managed digital identities, per-site accounts, + encrypted vault assets, capability-token secret brokering, and automated signup workflows. + + + DERIVED DOCUMENT: regenerate with `python scripts/export-openapi.py`. Do not hand-edit.' + license: + name: MIT + url: https://opensource.org/licenses/MIT + version: 2.0.0 +paths: + /api/v1/auth/register: + post: + tags: + - Authentication + summary: Register + description: Register a new user. + operationId: post_auth_register + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserCreate' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UserResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/auth/login: + post: + tags: + - Authentication + summary: Login + description: Authenticate user and return access token. + operationId: post_auth_login + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserLogin' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Token' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/auth/me: + get: + tags: + - Authentication + summary: Get Current User Info + description: Get current user information. + operationId: get_auth_me + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UserResponse' + security: + - HTTPBearer: [] + /api/v1/auth/logout: + post: + tags: + - Authentication + summary: Logout + description: Logout user (client should discard token). + operationId: post_auth_logout + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/identities/: + get: + tags: + - Identities + summary: List Identities + description: List all identities for the current user. + operationId: get_identities + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/IdentityListResponse' + type: array + title: Response List Identities Api V1 Identities Get + security: + - HTTPBearer: [] + post: + tags: + - Identities + summary: Create Identity + description: Create a new identity. + operationId: post_identities + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityCreate' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/identities/{identity_id}: + get: + tags: + - Identities + summary: Get Identity + description: Get a specific identity. + operationId: get_identities_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + put: + tags: + - Identities + summary: Update Identity + description: Update an identity. + operationId: put_identities_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - Identities + summary: Delete Identity + description: Delete an identity. + operationId: delete_identities_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/accounts/: + get: + tags: + - Accounts + summary: List Accounts + description: List all accounts for the current user with stage information. + operationId: get_accounts + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/AccountResponse' + type: array + title: Response List Accounts Api V1 Accounts Get + security: + - HTTPBearer: [] + post: + tags: + - Accounts + summary: Create Account + description: Create a new account with stages. + operationId: post_accounts + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCreate' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/accounts/{account_id}/stages/{stage_id}: + patch: + tags: + - Accounts + summary: Update Account Stage + description: Update a specific account stage. + operationId: patch_accounts_by_account_id_stages_by_stage_id + security: + - HTTPBearer: [] + parameters: + - name: account_id + in: path + required: true + schema: + type: integer + title: Account Id + - name: stage_id + in: path + required: true + schema: + type: integer + title: Stage Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountStageUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/automation/analyze: + post: + tags: + - Automation + summary: Analyze Website + description: Analyze a website's signup process. + operationId: post_automation_analyze + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeWebsiteRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AnalysisResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/chat/message: + post: + tags: + - Chat + summary: Chat Message + description: Process chat message and return response. + operationId: post_chat_message + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ChatMessage' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ChatResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/chat/signup: + post: + tags: + - Chat + summary: Initiate Signup + description: Initiate automated signup process. + operationId: post_chat_signup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/app__routers__chat__SignupRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ChatResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/sms/register-device: + post: + tags: + - SMS + - SMS + summary: Register Device + description: 'Register a new SMS interceptor device. + + + SECURITY / BOOTSTRAP TRUST ASSUMPTION: This endpoint is intentionally left + + UNauthenticated because it is the bootstrap that ISSUES the per-device API + + key — requiring the device key here would be a chicken-and-egg problem and + + break first-time registration. The trust assumption is therefore that + + registration is reachable only by intended devices (network/ingress policy + + or an out-of-band enrolment secret should gate it in production). Note it is + + also self-overwriting: re-registering an existing device_id rotates its key, + + so exposure should be limited by the surrounding network controls. + + PRODUCTION NOTE: add an enrolment token / mutual-TLS / signed attestation so + + arbitrary callers cannot register or hijack a device_id.' + operationId: post_sms_register_device + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DeviceRegistrationRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/otp: + post: + tags: + - SMS + - SMS + summary: Receive Otp + description: 'Receive OTP code from mobile app. + + + SECURITY: This endpoint accepts device-submitted data and is therefore + + authenticated. The submitting device must present the API key it was + + issued at registration (via the ``X-Device-Key`` header) and must specify + + which pending request it is fulfilling. The device is then verified to be + + the device that owns/was assigned that request. This closes the OTP + + hijacking hole where any unauthenticated caller could complete the + + "first waiting" request with an attacker-controlled OTP.' + operationId: post_sms_otp + parameters: + - name: X-Device-Key + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Device-Key + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OtpRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/requests/{device_id}: + get: + tags: + - SMS + - SMS + summary: Get Otp Requests + description: 'Get pending OTP requests for a device. + + + SECURITY: This is a DEVICE-to-server callback (the mobile interceptor polls + + for work it should fulfil), so it is authenticated with the per-device API + + key exactly like ``/otp`` — not the user JWT. The device must present the + + ``X-Device-Key`` it was issued at registration, and that key must belong to + + the ``device_id`` in the path, preventing one device from enumerating + + another device''s pending requests. Unknown/unauthenticated devices get 401.' + operationId: get_sms_requests_by_device_id + parameters: + - name: device_id + in: path + required: true + schema: + type: string + title: Device Id + - name: X-Device-Key + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Device-Key + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/request-otp: + post: + tags: + - SMS + - SMS + summary: Request Otp + description: 'Request an OTP for a specific service (called by your main app). + + + SECURITY: This is an OPERATOR/USER-facing action (the main app asks the + + platform to wait for an OTP for a given service), so it requires the + + application JWT via get_current_user. An unauthenticated caller could + + otherwise spam OTP requests / push fake jobs to devices. Devices do not + + call this endpoint, so device-key auth is not appropriate here.' + operationId: post_sms_request_otp + security: + - HTTPBearer: [] + parameters: + - name: service + in: query + required: true + schema: + type: string + title: Service + - name: timeout_seconds + in: query + required: false + schema: + type: integer + default: 300 + title: Timeout Seconds + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/request-status/{request_id}: + get: + tags: + - SMS + - SMS + summary: Get Request Status + description: 'Get the status of an OTP request. + + + SECURITY: This endpoint returns the received OTP value itself + + (``otp_code``), which is highly sensitive. It is operator/user-facing + + (the main app polls for the result), so it requires the application JWT. + + Leaving it unauthenticated would let any caller read OTP codes by guessing + + or enumerating request_ids. Device-key auth is not used because devices + + submit OTPs (via /otp), they do not read them back.' + operationId: get_sms_request_status_by_request_id + security: + - HTTPBearer: [] + parameters: + - name: request_id + in: path + required: true + schema: + type: string + title: Request Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/devices: + get: + tags: + - SMS + - SMS + summary: Get Devices + description: 'Get all registered SMS devices. + + + SECURITY: This lists the full device inventory (ids, names, OS/app + + versions, activity) which is sensitive operational/PII-ish data and an + + enumeration aid for attackers. It is an operator/admin view, so it requires + + the application JWT. Not a device callback, so device-key auth does not fit.' + operationId: get_sms_devices + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + security: + - HTTPBearer: [] + /api/sms/health: + get: + tags: + - SMS + - SMS + summary: Health Check + description: 'Health check endpoint. + + + SECURITY: Left unauthenticated by design — health/liveness probes are + + called by infrastructure (load balancers, k8s) before any auth context + + exists. It returns only coarse counts (active connections, pending request + + count), not OTP values, device ids, or other sensitive data, so no auth is + + required.' + operationId: get_sms_health + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/infrastructure/sms/request-verification: + post: + tags: + - Infrastructure + - Infrastructure + summary: Request Sms Verification + description: 'Request SMS verification from mobile device for scraper use. + + + SECURITY: Operator/app-facing — this initiates a verification job and + + broadcasts it to mobile devices. Requires the application JWT so arbitrary + + callers cannot push fake jobs to devices or exhaust resources. The + + request->device binding is established later, when a device authenticates + + and submits the code via /sms/complete-verification.' + operationId: post_infrastructure_sms_request_verification + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SmsVerificationRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + type: string + type: object + title: Response Request Sms Verification Api Infrastructure Sms Request Verification Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/infrastructure/sms/get-verification/{request_id}: + get: + tags: + - Infrastructure + - Infrastructure + summary: Get Sms Verification + description: 'Get SMS verification code for scraper. + + + SECURITY: Returns the verification CODE itself, which is highly sensitive. + + Operator/app-facing (the scraper orchestration polls for the result via the + + app), so it requires the application JWT. Previously unauthenticated, this + + let any caller read codes by enumerating request_ids.' + operationId: get_infrastructure_sms_get_verification_by_request_id + security: + - HTTPBearer: [] + parameters: + - name: request_id + in: path + required: true + schema: + type: string + title: Request Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/sms/complete-verification/{request_id}: + post: + tags: + - Infrastructure + - Infrastructure + summary: Complete Sms Verification + description: "Called by mobile device to complete SMS verification.\n\nSECURITY: This is a DEVICE-to-server\ + \ callback that submits an\nattacker-influenceable value (the verification ``code``). It is therefore\n\ + authenticated with the SAME per-device API key model as sms.py:\n\n1) AUTH — the submitting device\ + \ must present the ``X-Device-Key`` it was\n issued at registration, verified against ``device_id``\ + \ via the shared\n ``_verify_device``. Unknown/unauthenticated devices are rejected 401.\n \ + \ This closes the hole where ANY unauthenticated caller could complete a\n verification with\ + \ an attacker-controlled code.\n2) BINDING — the request is bound to a specific device. infrastructure.py\n\ + \ owns its own request store (``verification_requests``), and that store\n had no device assignment\ + \ at request time, so (mirroring sms.py's /otp)\n we bind on first authenticated completion:\ + \ the first authenticated\n device to answer becomes the assigned device, and a DIFFERENT device\n\ + \ attempting to complete the same request is rejected 403. This prevents\n a second (even\ + \ authenticated) device from overwriting another device's\n in-flight verification." + operationId: post_infrastructure_sms_complete_verification_by_request_id + parameters: + - name: request_id + in: path + required: true + schema: + type: string + title: Request Id + - name: code + in: query + required: true + schema: + type: string + title: Code + - name: device_id + in: query + required: true + schema: + type: string + title: Device Id + - name: X-Device-Key + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Device-Key + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/email/setup-monitoring: + post: + tags: + - Infrastructure + - Infrastructure + summary: Setup Email Monitoring + description: 'Setup email monitoring for verification emails. + + + SECURITY: Operator/app-facing — it registers monitoring on an email + + address and patterns (sensitive targeting data). Requires the application + + JWT; not a device callback.' + operationId: post_infrastructure_email_setup_monitoring + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmailMonitoringRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + type: string + type: object + title: Response Setup Email Monitoring Api Infrastructure Email Setup Monitoring Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/infrastructure/email/get-verification/{monitor_id}: + get: + tags: + - Infrastructure + - Infrastructure + summary: Get Email Verification + description: 'Get email verification content. + + + SECURITY: Returns captured email content (``found_emails``), which is + + sensitive. Operator/app-facing, so it requires the application JWT. + + Previously unauthenticated, allowing enumeration of monitor_ids to read + + intercepted emails.' + operationId: get_infrastructure_email_get_verification_by_monitor_id + security: + - HTTPBearer: [] + parameters: + - name: monitor_id + in: path + required: true + schema: + type: string + title: Monitor Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/mobile/send-command: + post: + tags: + - Infrastructure + - Infrastructure + summary: Send Mobile Command + description: 'Send command to mobile device for UI automation. + + + SECURITY: Operator/app-facing — this dispatches automation commands to + + devices. Requires the application JWT so an unauthenticated caller cannot + + drive devices / inject commands.' + operationId: post_infrastructure_mobile_send_command + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MobileCommandRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + type: string + type: object + title: Response Send Mobile Command Api Infrastructure Mobile Send Command Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/infrastructure/mobile/get-command-result/{command_id}: + get: + tags: + - Infrastructure + - Infrastructure + summary: Get Mobile Command Result + description: 'Get result of mobile command execution. + + + SECURITY: Operator/app-facing. Although currently a stub, it is designed to + + surface results returned by mobile devices (potentially sensitive), so it + + is gated by the application JWT now to avoid shipping an unauthenticated + + read endpoint once result tracking is implemented.' + operationId: get_infrastructure_mobile_get_command_result_by_command_id + security: + - HTTPBearer: [] + parameters: + - name: command_id + in: path + required: true + schema: + type: string + title: Command Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/scraper/report-error: + post: + tags: + - Infrastructure + - Infrastructure + summary: Report Scraper Error + description: 'Report scraper execution error for analysis. + + + SECURITY: Accepts arbitrary external input (scraper_id + free-form data). + + Scrapers run under the platform/operator identity, so this requires the + + application JWT to prevent unauthenticated log/data injection and spam. + + Not a mobile-device callback, so device-key auth does not apply.' + operationId: post_infrastructure_scraper_report_error + security: + - HTTPBearer: [] + parameters: + - name: scraper_id + in: query + required: true + schema: + type: string + title: Scraper Id + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + title: Error Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/scraper/report-success: + post: + tags: + - Infrastructure + - Infrastructure + summary: Report Scraper Success + description: 'Report scraper execution success. + + + SECURITY: Accepts arbitrary external input; gated by the application JWT for + + the same reasons as /scraper/report-error (the scraper acts as the + + platform/operator). Not a device callback.' + operationId: post_infrastructure_scraper_report_success + security: + - HTTPBearer: [] + parameters: + - name: scraper_id + in: query + required: true + schema: + type: string + title: Scraper Id + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + title: Success Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/llm-scraper/generate: + post: + tags: + - LLM Scraper + - LLM Scraper + summary: Generate Scraper + description: Generate a new scraper for a site + operationId: post_llm_scraper_generate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GenerateScraperRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/llm-scraper/improve: + post: + tags: + - LLM Scraper + - LLM Scraper + summary: Improve Scraper + description: Improve an existing scraper based on execution results + operationId: post_llm_scraper_improve + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImproveScraperRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/llm-scraper/scrapers: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: List Scrapers + description: List all generated scrapers + operationId: get_llm_scraper_scrapers + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperListResponse' + security: + - HTTPBearer: [] + /api/llm-scraper/scrapers/{site_name}/{action_type}: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: Get Scraper + description: Get a specific scraper + operationId: get_llm_scraper_scrapers_by_site_name_by_action_type + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + - name: action_type + in: path + required: true + schema: + type: string + title: Action Type + - name: version + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Version + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - LLM Scraper + - LLM Scraper + summary: Delete Scraper + description: Delete a scraper and all its versions + operationId: delete_llm_scraper_scrapers_by_site_name_by_action_type + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + - name: action_type + in: path + required: true + schema: + type: string + title: Action Type + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/llm-scraper/scrapers/{site_name}/{action_type}/history: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: Get Scraper History + description: Get the full history of a scraper + operationId: get_llm_scraper_scrapers_by_site_name_by_action_type_history + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + - name: action_type + in: path + required: true + schema: + type: string + title: Action Type + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/llm-scraper/stats: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: Get Generation Stats + description: Get statistics about scraper generation + operationId: get_llm_scraper_stats + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + security: + - HTTPBearer: [] + /api/llm-scraper/debug: + post: + tags: + - LLM Scraper + - LLM Scraper + summary: Debug Scraper + description: Debug a specific scraper issue + operationId: post_llm_scraper_debug + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: query + required: true + schema: + type: string + title: Site Name + - name: action_type + in: query + required: true + schema: + type: string + title: Action Type + - name: error + in: query + required: true + schema: + type: string + title: Error + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + title: Context + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/request-identity-credentials: + post: + tags: + - Credentials + - Credentials + summary: 🔐 Generate Credentials for Identity + description: 'Generate fresh credentials for an identity to sign up for a specific site. + + + This endpoint creates site-appropriate credentials based on the identity''s information + + and the target website''s requirements. Perfect for automated signup processes. + + + **Use Cases:** + + - Generate signup credentials for new accounts + + - Create site-specific usernames and emails + + - Get credentials for scraper automation + + + **Security:** All requests are logged with service identity for audit purposes.' + operationId: post_credentials_request_identity_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialRequest' + responses: + '200': + description: Credentials generated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialResponse' + '401': + description: Invalid API key + '404': + description: Identity not found + '500': + description: Failed to generate credentials + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/request-account-credentials: + post: + tags: + - Credentials + - Credentials + summary: 🔑 Retrieve Stored Account Credentials + description: 'Retrieve stored credentials for an existing account. + + + Perfect for accessing previously created accounts where credentials have been + + stored after successful signup. + + + **Use Cases:** + + - Get credentials for existing account signin + + - Retrieve API keys for service integration + + - Access 2FA backup codes + + + **Security:** Updates last accessed timestamp and logs access for audit.' + operationId: post_credentials_request_account_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCredentialRequest' + responses: + '200': + description: Credentials retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCredentialResponse' + '401': + description: Invalid API key + '404': + description: Account not found + '500': + description: Failed to retrieve credentials + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/store-account-credentials: + post: + tags: + - Credentials + - Credentials + summary: 💾 Store Account Credentials + description: 'Store or update credentials for an account after successful signup. + + + Use this endpoint to securely store credentials after a successful account + + creation process. All credentials are encrypted before storage. + + + **Use Cases:** + + - Store credentials after successful signup + + - Update credentials with new API keys + + - Save 2FA backup codes and recovery information + + + **Security:** All credentials are encrypted using Fernet (AES 128) before storage.' + operationId: post_credentials_store_account_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialUpdate' + responses: + '200': + description: Credentials stored successfully + content: + application/json: + schema: {} + '401': + description: Invalid API key + '404': + description: Account not found + '500': + description: Failed to store credentials + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/account/{account_id}/credentials: + get: + tags: + - Credentials + - Credentials + summary: 📋 Get Account Credentials (GET) + description: 'Alternative GET endpoint for retrieving account credentials. + + + Convenient endpoint for getting credentials when you know the account ID.' + operationId: get_credentials_account_by_account_id_credentials + parameters: + - name: account_id + in: path + required: true + schema: + type: integer + description: Account ID to retrieve credentials for + title: Account Id + description: Account ID to retrieve credentials for + - name: identity_id + in: query + required: true + schema: + type: integer + description: ID of the identity that owns the account + title: Identity Id + description: ID of the identity that owns the account + - name: credential_types + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Comma-separated list of credential types to retrieve + title: Credential Types + description: Comma-separated list of credential types to retrieve + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCredentialResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/identity/{identity_id}/accounts: + get: + tags: + - Credentials + - Credentials + summary: 👤 List Identity Accounts + description: 'Get all accounts associated with a specific identity. + + + Useful for understanding what accounts an identity has created and their status.' + operationId: get_credentials_identity_by_identity_id_accounts + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + description: Identity ID to get accounts for + title: Identity Id + description: Identity ID to get accounts for + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/validate-credentials: + post: + tags: + - Credentials + - Credentials + summary: ✅ Validate Credential Format + description: 'Validate credentials format for a specific site. + + + Check if credentials meet the requirements for a particular website before + + attempting to use them. + + + **Validation includes:** + + - Required field presence + + - Email format validation + + - Password strength requirements + + - Site-specific username patterns' + operationId: post_credentials_validate_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationResult' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/health: + get: + tags: + - Credentials + - Credentials + summary: 🏥 Health Check + description: Check the health status of the credentials service + operationId: get_credentials_health + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/google/signup/{identity_id}: + post: + tags: + - Google Integration + - Google Integration + summary: Signup With Identity + description: 'Create a Google account using the specified identity. + + + SECURITY: requires auth and that the identity is owned by the caller.' + operationId: post_google_signup_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GoogleSignupConfig' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Signup With Identity Api Google Signup Identity Id Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/google/signup/manual: + post: + tags: + - Google Integration + - Google Integration + summary: Manual Signup + description: 'Create a Google account with manually provided data. + + + SECURITY: requires auth. This route drives real signup automation from the + + request body and must not be reachable unauthenticated.' + operationId: post_google_signup_manual + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Body_manual_signup_api_google_signup_manual_post' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response Manual Signup Api Google Signup Manual Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/google/config/default: + get: + tags: + - Google Integration + - Google Integration + summary: Get Default Config + description: 'Get the default configuration for Google signup. + + + SECURITY: requires auth. Even though it returns no PII, keep the whole + + router authenticated so there is no unauthenticated surface here.' + operationId: get_google_config_default + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GoogleSignupConfig' + security: + - HTTPBearer: [] + /api/google/test/identity-conversion/{identity_id}: + post: + tags: + - Google Integration + - Google Integration + summary: Test Identity Conversion + description: 'Test converting an identity to Google signup data without actually creating an account. + + + SECURITY (CRITICAL-1): this endpoint returns DECRYPTED PII (names, username, + + phone, recovery email, birth date, gender). It now requires auth and that the + + identity belongs to the caller; otherwise 404. This closes the unauthenticated + + PII-exfiltration path.' + operationId: post_google_test_identity_conversion_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Test Identity Conversion Api Google Test Identity Conversion Identity + Id Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/google/accounts/{identity_id}: + get: + tags: + - Google Integration + - Google Integration + summary: Get Google Accounts + description: 'Get all Google accounts for a specific identity. + + + SECURITY (CRITICAL-1): requires auth; the identity must be owned by the + + caller (404 otherwise). The account lookup is additionally scoped through the + + owned identity so no cross-tenant account data is returned.' + operationId: get_google_accounts_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Google Accounts Api Google Accounts Identity Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/integrations/sites: + get: + tags: + - Site Integrations + - Site Integrations + summary: List Available Sites + description: Get a list of all available site integrations. + operationId: get_integrations_sites + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + items: + type: string + type: array + type: object + title: Response List Available Sites Api V1 Integrations Sites Get + /api/v1/integrations/sites/{site_name}/capabilities: + get: + tags: + - Site Integrations + - Site Integrations + summary: Get Site Capabilities Endpoint + description: Get the capabilities of a specific site integration. + operationId: get_integrations_sites_by_site_name_capabilities + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/integrations/signup: + post: + tags: + - Site Integrations + - Site Integrations + summary: Create Account + description: 'Create a new account on the specified site. + + + This endpoint handles automated account signup for supported sites. + + The operation runs in the background for long-running automations. + + + SECURITY (HIGH-1 / appsec #18): requires auth. This drives credential-bearing + + browser automation against external sites; an unauthenticated caller must not + + be able to trigger it (abuse / resource exhaustion).' + operationId: post_integrations_signup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/app__routers__site_integrations__SignupRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IntegrationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/integrations/signin: + post: + tags: + - Site Integrations + - Site Integrations + summary: Authenticate Account + description: 'Authenticate with an existing account on the specified site. + + + This endpoint handles automated authentication for supported sites. + + + SECURITY (HIGH-1 / appsec #18): requires auth. This accepts raw email+password + + and drives credential-bearing browser automation against external sites; an + + unauthenticated caller must not be able to trigger it (abuse / resource + + exhaustion / credential-stuffing surface).' + operationId: post_integrations_signin + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SigninRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IntegrationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/integrations/apikey: + post: + tags: + - Site Integrations + - Site Integrations + summary: Create Api Key + description: 'Create an API key for the specified site account. + + + This endpoint handles automated API key creation for supported sites. + + + SECURITY (HIGH-1 / appsec #18): requires auth. This accepts raw email+password + + and drives credential-bearing browser automation against external sites; an + + unauthenticated caller must not be able to trigger it.' + operationId: post_integrations_apikey + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKeyRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IntegrationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/integrations/health: + get: + tags: + - Site Integrations + - Site Integrations + summary: Integration Health Check + description: Health check endpoint for site integrations. + operationId: get_integrations_health + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/broker/grant: + post: + tags: + - Secret Broker + - Secret Broker + summary: Grant + description: Issue a grant. The grantor is the caller's authenticated transport identity. + operationId: post_broker_grant + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GrantRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/broker/redeem: + post: + tags: + - Secret Broker + - Secret Broker + summary: Redeem + operationId: post_broker_redeem + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RedeemRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/broker/mint-token: + post: + tags: + - Secret Broker + - Secret Broker + summary: Mint Token + operationId: post_broker_mint_token + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MintTokenRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/broker/revoke: + post: + tags: + - Secret Broker + - Secret Broker + summary: Revoke + operationId: post_broker_revoke + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RevokeRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/sites/categories: + get: + tags: + - Sites Management + - Sites Management + summary: List Categories + description: Get list of all categories. + operationId: get_sites_categories + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/sites/stats/overview: + get: + tags: + - Sites Management + - Sites Management + summary: Get Sites Overview + description: Get overview statistics of sites. + operationId: get_sites_stats_overview + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/sites/: + get: + tags: + - Sites Management + - Sites Management + summary: List Sites + description: Get list of sites with filtering and pagination. + operationId: get_sites + parameters: + - name: skip + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: Skip + - name: limit + in: query + required: false + schema: + type: integer + maximum: 1000 + minimum: 1 + default: 100 + title: Limit + - name: category + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Category + - name: difficulty + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Difficulty + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + - name: priority_min + in: query + required: false + schema: + anyOf: + - type: integer + maximum: 100 + minimum: 1 + - type: 'null' + title: Priority Min + - name: search + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Search + - name: sort_by + in: query + required: false + schema: + type: string + pattern: ^(name|priority|difficulty|progress|created_at)$ + default: priority + title: Sort By + - name: sort_order + in: query + required: false + schema: + type: string + pattern: ^(asc|desc)$ + default: desc + title: Sort Order + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SiteResponse' + title: Response List Sites Api V1 Sites Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/sites/{site_id}: + get: + tags: + - Sites Management + - Sites Management + summary: Get Site + description: Get specific site by ID. + operationId: get_sites_by_site_id + parameters: + - name: site_id + in: path + required: true + schema: + type: integer + title: Site Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/SiteResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /: + get: + summary: API Health Check + description: Root endpoint that returns API status and basic information + operationId: get_root + responses: + '200': + description: API status and version information + content: + application/json: + schema: {} + /health: + get: + summary: Detailed Health Check + description: Comprehensive health check with service status details + operationId: get_health + responses: + '200': + description: Detailed health status of all services + content: + application/json: + schema: {} + /api/v1/demo/identities: + get: + tags: + - Demo + summary: Demo Identities + description: Sample identity data for testing and demonstration + operationId: get_demo_identities + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/demo/accounts: + get: + tags: + - Demo + summary: Demo Accounts + description: Sample account data for testing and demonstration + operationId: get_demo_accounts + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/demo/chat: + post: + tags: + - Demo + summary: Demo Chat + description: Demo AI chat interface for testing conversational features + operationId: post_demo_chat + requestBody: + content: + application/json: + schema: + additionalProperties: true + type: object + title: Message + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' +components: + schemas: + AccountCreate: + properties: + website_name: + type: string + title: Website Name + website_url: + type: string + title: Website Url + identity_id: + type: integer + title: Identity Id + website_domain: + anyOf: + - type: string + - type: 'null' + title: Website Domain + account_type: + anyOf: + - type: string + - type: 'null' + title: Account Type + default: free + signup_method: + anyOf: + - type: string + - type: 'null' + title: Signup Method + default: automated + stages: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Stages + type: object + required: + - website_name + - website_url + - identity_id + title: AccountCreate + AccountCredentialRequest: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity that owns the account + example: 1 + account_id: + type: integer + title: Account Id + description: ID of the account to retrieve credentials for + example: 1 + credential_types: + items: + type: string + type: array + title: Credential Types + description: List of credential types to retrieve + example: + - email + - password + type: object + required: + - identity_id + - account_id + - credential_types + title: AccountCredentialRequest + description: Request model for retrieving stored account credentials + example: + account_id: 1 + credential_types: + - email + - password + - api_key + identity_id: 1 + AccountCredentialResponse: + properties: + account_id: + type: integer + title: Account Id + description: ID of the account + site_name: + type: string + title: Site Name + description: Name of the website + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Stored credentials + last_used: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Last Used + description: Timestamp when credentials were last accessed + type: object + required: + - account_id + - site_name + - credentials + title: AccountCredentialResponse + description: Response model for stored account credentials + example: + account_id: 1 + credentials: + api_key: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + email: john.doe.1703123456@example.com + password: JohnDoePass123! + last_used: '2024-01-01T12:00:00Z' + site_name: github + AccountResponse: + properties: + id: + type: integer + title: Id + website_name: + type: string + title: Website Name + website_url: + type: string + title: Website Url + identity_id: + type: integer + title: Identity Id + identity_name: + type: string + title: Identity Name + is_active: + type: boolean + title: Is Active + signup_completed: + type: boolean + title: Signup Completed + created_at: + type: string + format: date-time + title: Created At + stages: + items: + $ref: '#/components/schemas/StageStatusResponse' + type: array + title: Stages + type: object + required: + - id + - website_name + - website_url + - identity_id + - identity_name + - is_active + - signup_completed + - created_at + - stages + title: AccountResponse + AccountStageUpdate: + properties: + status: + type: string + title: Status + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + stage_data: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Stage Data + type: object + required: + - status + title: AccountStageUpdate + AnalysisResponse: + properties: + success: + type: boolean + title: Success + message: + type: string + title: Message + details: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Details + type: object + required: + - success + - message + title: AnalysisResponse + AnalyzeWebsiteRequest: + properties: + url: + type: string + title: Url + type: object + required: + - url + title: AnalyzeWebsiteRequest + ApiKeyRequest: + properties: + site: + type: string + title: Site + email: + type: string + format: email + title: Email + password: + type: string + title: Password + key_name: + type: string + title: Key Name + default: FuzeKeys + headless: + type: boolean + title: Headless + default: true + type: object + required: + - site + - email + - password + title: ApiKeyRequest + Body_manual_signup_api_google_signup_manual_post: + properties: + signup_data: + $ref: '#/components/schemas/GoogleSignupData' + config: + $ref: '#/components/schemas/GoogleSignupConfig' + type: object + required: + - signup_data + title: Body_manual_signup_api_google_signup_manual_post + ChatMessage: + properties: + message: + type: string + title: Message + type: object + required: + - message + title: ChatMessage + ChatResponse: + properties: + response: + type: string + title: Response + action_type: + anyOf: + - type: string + - type: 'null' + title: Action Type + suggested_actions: + items: + type: string + type: array + title: Suggested Actions + default: [] + automation_status: + anyOf: + - type: string + - type: 'null' + title: Automation Status + type: object + required: + - response + title: ChatResponse + CredentialRequest: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity to generate credentials for + example: 1 + site_name: + type: string + title: Site Name + description: Name of the target website + example: github + action_type: + type: string + title: Action Type + description: Type of action being performed + example: signup + credential_types: + items: + type: string + type: array + title: Credential Types + description: List of credential types needed + example: + - email + - password + - username + type: object + required: + - identity_id + - site_name + - action_type + - credential_types + title: CredentialRequest + description: Request model for generating credentials for an identity + example: + action_type: signup + credential_types: + - email + - password + - username + - name + identity_id: 1 + site_name: github + CredentialResponse: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity + site_name: + type: string + title: Site Name + description: Name of the target website + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Generated credentials + metadata: + additionalProperties: true + type: object + title: Metadata + description: Request metadata and tracking information + type: object + required: + - identity_id + - site_name + - credentials + - metadata + title: CredentialResponse + description: Response model for generated credentials + example: + credentials: + email: john.doe.1703123456@example.com + name: John Doe + password: JohnDoePass123! + username: johndoe_a1b2 + identity_id: 1 + metadata: + action_type: signup + credential_types: + - email + - password + - username + - name + requested_at: '2024-01-01T12:00:00Z' + requested_by: scraper-service + site_name: github + CredentialUpdate: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity that owns the account + account_id: + type: integer + title: Account Id + description: ID of the account to store credentials for + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Credentials to store + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Metadata + description: Additional metadata about the credentials + type: object + required: + - identity_id + - account_id + - credentials + title: CredentialUpdate + description: Request model for storing/updating account credentials + example: + account_id: 1 + credentials: + api_key: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + email: john.doe.1703123456@example.com + password: JohnDoePass123! + username: johndoe_a1b2 + identity_id: 1 + metadata: + account_type: free + signup_date: '2024-01-01T12:00:00Z' + verification_method: email + DeviceRegistrationRequest: + properties: + device_id: + type: string + title: Device Id + device_name: + type: string + title: Device Name + os_version: + type: string + title: Os Version + app_version: + type: string + title: App Version + type: object + required: + - device_id + - device_name + - os_version + - app_version + title: DeviceRegistrationRequest + EmailMonitoringRequest: + properties: + email: + type: string + title: Email + sender_patterns: + items: + type: string + type: array + title: Sender Patterns + subject_patterns: + items: + type: string + type: array + title: Subject Patterns + timeout_seconds: + type: integer + title: Timeout Seconds + default: 300 + type: object + required: + - email + - sender_patterns + - subject_patterns + title: EmailMonitoringRequest + GenerateScraperRequest: + properties: + site_name: + type: string + title: Site Name + site_url: + type: string + title: Site Url + action_type: + type: string + title: Action Type + patterns: + additionalProperties: true + type: object + title: Patterns + default: {} + test_data: + additionalProperties: true + type: object + title: Test Data + default: {} + type: object + required: + - site_name + - site_url + - action_type + title: GenerateScraperRequest + GoogleSignupConfig: + properties: + use_proxy: + type: boolean + title: Use Proxy + default: false + proxy_config: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Proxy Config + headless: + type: boolean + title: Headless + default: true + timeout: + type: integer + title: Timeout + default: 120 + retry_attempts: + type: integer + title: Retry Attempts + default: 3 + use_mobile_user_agent: + type: boolean + title: Use Mobile User Agent + default: false + custom_user_agent: + anyOf: + - type: string + - type: 'null' + title: Custom User Agent + prefer_phone_verification: + type: boolean + title: Prefer Phone Verification + default: true + auto_handle_captcha: + type: boolean + title: Auto Handle Captcha + default: false + save_cookies: + type: boolean + title: Save Cookies + default: true + type: object + title: GoogleSignupConfig + description: Configuration for Google signup process. + GoogleSignupData: + properties: + first_name: + type: string + maxLength: 50 + minLength: 1 + title: First Name + last_name: + type: string + maxLength: 50 + minLength: 1 + title: Last Name + username: + type: string + maxLength: 30 + minLength: 6 + title: Username + password: + type: string + minLength: 8 + title: Password + phone_number: + anyOf: + - type: string + - type: 'null' + title: Phone Number + description: Phone number for account recovery + recovery_email: + anyOf: + - type: string + - type: 'null' + title: Recovery Email + description: Recovery email address + birth_date: + anyOf: + - type: string + format: date + - type: 'null' + title: Birth Date + description: Date of birth (required for some regions) + gender: + anyOf: + - type: string + - type: 'null' + title: Gender + description: Gender (optional) + interests: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Interests + description: User interests + skip_phone_verification: + type: boolean + title: Skip Phone Verification + description: Try to skip phone verification if possible + default: false + type: object + required: + - first_name + - last_name + - username + - password + title: GoogleSignupData + description: Data structure for Google account signup. + GrantRequest: + properties: + redeemer_identity: + type: string + title: Redeemer Identity + description: Bound transport identity that may redeem + scope: + additionalProperties: true + type: object + title: Scope + ttl_seconds: + anyOf: + - type: integer + - type: 'null' + title: Ttl Seconds + description: Clamped to the server maximum + secret_ref: + anyOf: + - type: string + - type: 'null' + title: Secret Ref + operation: + anyOf: + - type: string + - type: 'null' + title: Operation + single_use: + type: boolean + title: Single Use + default: true + sensitivity: + type: string + title: Sensitivity + default: medium + type: object + required: + - redeemer_identity + title: GrantRequest + HTTPValidationError: + properties: + detail: + items: + $ref: '#/components/schemas/ValidationError' + type: array + title: Detail + type: object + title: HTTPValidationError + IdentityCreate: + properties: + name: + type: string + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + first_name: + type: string + title: First Name + last_name: + type: string + title: Last Name + email: + type: string + format: email + title: Email + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + date_of_birth: + anyOf: + - type: string + - type: 'null' + title: Date Of Birth + address_line1: + anyOf: + - type: string + - type: 'null' + title: Address Line1 + address_line2: + anyOf: + - type: string + - type: 'null' + title: Address Line2 + city: + anyOf: + - type: string + - type: 'null' + title: City + state: + anyOf: + - type: string + - type: 'null' + title: State + zip_code: + anyOf: + - type: string + - type: 'null' + title: Zip Code + country: + anyOf: + - type: string + - type: 'null' + title: Country + profession: + anyOf: + - type: string + - type: 'null' + title: Profession + company: + anyOf: + - type: string + - type: 'null' + title: Company + bio: + anyOf: + - type: string + - type: 'null' + title: Bio + custom_fields: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Custom Fields + preferred_username_pattern: + anyOf: + - type: string + - type: 'null' + title: Preferred Username Pattern + type: object + required: + - name + - first_name + - last_name + - email + title: IdentityCreate + IdentityListResponse: + properties: + id: + type: integer + title: Id + name: + type: string + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + created_at: + type: string + format: date-time + title: Created At + type: object + required: + - id + - name + - description + - created_at + title: IdentityListResponse + IdentityResponse: + properties: + id: + type: integer + title: Id + name: + type: string + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + first_name: + type: string + title: First Name + last_name: + type: string + title: Last Name + email: + type: string + title: Email + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + date_of_birth: + anyOf: + - type: string + - type: 'null' + title: Date Of Birth + address_line1: + anyOf: + - type: string + - type: 'null' + title: Address Line1 + address_line2: + anyOf: + - type: string + - type: 'null' + title: Address Line2 + city: + anyOf: + - type: string + - type: 'null' + title: City + state: + anyOf: + - type: string + - type: 'null' + title: State + zip_code: + anyOf: + - type: string + - type: 'null' + title: Zip Code + country: + anyOf: + - type: string + - type: 'null' + title: Country + profession: + anyOf: + - type: string + - type: 'null' + title: Profession + company: + anyOf: + - type: string + - type: 'null' + title: Company + bio: + anyOf: + - type: string + - type: 'null' + title: Bio + custom_fields: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Custom Fields + preferred_username_pattern: + anyOf: + - type: string + - type: 'null' + title: Preferred Username Pattern + created_at: + type: string + format: date-time + title: Created At + updated_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Updated At + type: object + required: + - id + - name + - description + - first_name + - last_name + - email + - phone + - date_of_birth + - address_line1 + - address_line2 + - city + - state + - zip_code + - country + - profession + - company + - bio + - custom_fields + - preferred_username_pattern + - created_at + - updated_at + title: IdentityResponse + IdentityUpdate: + properties: + name: + anyOf: + - type: string + - type: 'null' + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + first_name: + anyOf: + - type: string + - type: 'null' + title: First Name + last_name: + anyOf: + - type: string + - type: 'null' + title: Last Name + email: + anyOf: + - type: string + format: email + - type: 'null' + title: Email + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + date_of_birth: + anyOf: + - type: string + - type: 'null' + title: Date Of Birth + address_line1: + anyOf: + - type: string + - type: 'null' + title: Address Line1 + address_line2: + anyOf: + - type: string + - type: 'null' + title: Address Line2 + city: + anyOf: + - type: string + - type: 'null' + title: City + state: + anyOf: + - type: string + - type: 'null' + title: State + zip_code: + anyOf: + - type: string + - type: 'null' + title: Zip Code + country: + anyOf: + - type: string + - type: 'null' + title: Country + profession: + anyOf: + - type: string + - type: 'null' + title: Profession + company: + anyOf: + - type: string + - type: 'null' + title: Company + bio: + anyOf: + - type: string + - type: 'null' + title: Bio + custom_fields: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Custom Fields + preferred_username_pattern: + anyOf: + - type: string + - type: 'null' + title: Preferred Username Pattern + type: object + title: IdentityUpdate + ImproveScraperRequest: + properties: + site_name: + type: string + title: Site Name + action_type: + type: string + title: Action Type + execution_result: + additionalProperties: true + type: object + title: Execution Result + type: object + required: + - site_name + - action_type + - execution_result + title: ImproveScraperRequest + IntegrationResponse: + properties: + success: + type: boolean + title: Success + message: + type: string + title: Message + data: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Data + error: + anyOf: + - type: string + - type: 'null' + title: Error + site: + type: string + title: Site + type: object + required: + - success + - message + - site + title: IntegrationResponse + MintTokenRequest: + properties: + audience: + type: string + title: Audience + scope: + type: string + title: Scope + ttl_seconds: + anyOf: + - type: integer + - type: 'null' + title: Ttl Seconds + type: object + required: + - audience + - scope + title: MintTokenRequest + MobileCommandRequest: + properties: + command_type: + type: string + title: Command Type + parameters: + additionalProperties: true + type: object + title: Parameters + timeout_seconds: + type: integer + title: Timeout Seconds + default: 60 + type: object + required: + - command_type + - parameters + title: MobileCommandRequest + OtpRequest: + properties: + otp: + type: string + title: Otp + sender: + type: string + title: Sender + message_body: + type: string + title: Message Body + timestamp: + type: integer + title: Timestamp + device_id: + type: string + title: Device Id + confidence: + anyOf: + - type: number + - type: 'null' + title: Confidence + request_id: + type: string + title: Request Id + type: object + required: + - otp + - sender + - message_body + - timestamp + - device_id + - request_id + title: OtpRequest + RedeemRequest: + properties: + grant_handle: + type: string + title: Grant Handle + type: object + required: + - grant_handle + title: RedeemRequest + RevokeRequest: + properties: + grant_id: + type: string + title: Grant Id + reason: + type: string + title: Reason + default: revoked + type: object + required: + - grant_id + title: RevokeRequest + ScraperListResponse: + properties: + scrapers: + items: + additionalProperties: true + type: object + type: array + title: Scrapers + total_count: + type: integer + title: Total Count + type: object + required: + - scrapers + - total_count + title: ScraperListResponse + ScraperResponse: + properties: + success: + type: boolean + title: Success + scraper_id: + type: string + title: Scraper Id + version: + type: integer + title: Version + code: + anyOf: + - type: string + - type: 'null' + title: Code + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Metadata + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + type: object + required: + - success + - scraper_id + - version + title: ScraperResponse + SigninRequest: + properties: + site: + type: string + title: Site + email: + type: string + format: email + title: Email + password: + type: string + title: Password + headless: + type: boolean + title: Headless + default: true + type: object + required: + - site + - email + - password + title: SigninRequest + SiteResponse: + properties: + id: + type: integer + title: Id + name: + type: string + title: Name + display_name: + type: string + title: Display Name + url: + type: string + title: Url + logo_url: + anyOf: + - type: string + - type: 'null' + title: Logo Url + category: + type: string + title: Category + description: + anyOf: + - type: string + - type: 'null' + title: Description + signup_difficulty: + type: string + title: Signup Difficulty + signin_difficulty: + type: string + title: Signin Difficulty + apikey_difficulty: + type: string + title: Apikey Difficulty + overall_difficulty: + type: string + title: Overall Difficulty + requires_email_verification: + type: boolean + title: Requires Email Verification + requires_phone_verification: + type: boolean + title: Requires Phone Verification + requires_sms_verification: + type: boolean + title: Requires Sms Verification + requires_authenticator: + type: boolean + title: Requires Authenticator + has_captcha: + type: boolean + title: Has Captcha + captcha_type: + anyOf: + - type: string + - type: 'null' + title: Captcha Type + anti_bot_techniques: + items: + type: string + type: array + title: Anti Bot Techniques + signup_status: + type: string + title: Signup Status + signin_status: + type: string + title: Signin Status + apikey_status: + type: string + title: Apikey Status + implementation_progress: + type: number + title: Implementation Progress + priority: + type: integer + title: Priority + estimated_hours: + anyOf: + - type: integer + - type: 'null' + title: Estimated Hours + has_official_api: + type: boolean + title: Has Official Api + api_documentation_url: + anyOf: + - type: string + - type: 'null' + title: Api Documentation Url + api_rate_limits: + anyOf: + - type: string + - type: 'null' + title: Api Rate Limits + notes: + anyOf: + - type: string + - type: 'null' + title: Notes + created_at: + anyOf: + - type: string + - type: 'null' + title: Created At + updated_at: + anyOf: + - type: string + - type: 'null' + title: Updated At + type: object + required: + - id + - name + - display_name + - url + - logo_url + - category + - description + - signup_difficulty + - signin_difficulty + - apikey_difficulty + - overall_difficulty + - requires_email_verification + - requires_phone_verification + - requires_sms_verification + - requires_authenticator + - has_captcha + - captcha_type + - anti_bot_techniques + - signup_status + - signin_status + - apikey_status + - implementation_progress + - priority + - estimated_hours + - has_official_api + - api_documentation_url + - api_rate_limits + - notes + - created_at + - updated_at + title: SiteResponse + SmsVerificationRequest: + properties: + site: + type: string + title: Site + phone_number: + type: string + title: Phone Number + timeout_seconds: + type: integer + title: Timeout Seconds + default: 300 + type: object + required: + - site + - phone_number + title: SmsVerificationRequest + StageStatusResponse: + properties: + stage_type: + type: string + title: Stage Type + stage_name: + type: string + title: Stage Name + status: + type: string + title: Status + attempts: + type: integer + title: Attempts + started_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Started At + completed_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Completed At + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + type: object + required: + - stage_type + - stage_name + - status + - attempts + - started_at + - completed_at + - error_message + title: StageStatusResponse + Token: + properties: + access_token: + type: string + title: Access Token + token_type: + type: string + title: Token Type + type: object + required: + - access_token + - token_type + title: Token + UserCreate: + properties: + username: + type: string + title: Username + email: + type: string + format: email + title: Email + password: + type: string + title: Password + master_key: + type: string + title: Master Key + first_name: + anyOf: + - type: string + - type: 'null' + title: First Name + last_name: + anyOf: + - type: string + - type: 'null' + title: Last Name + type: object + required: + - username + - email + - password + - master_key + title: UserCreate + UserLogin: + properties: + email: + type: string + format: email + title: Email + password: + type: string + title: Password + master_key: + type: string + title: Master Key + type: object + required: + - email + - password + - master_key + title: UserLogin + UserResponse: + properties: + id: + type: integer + title: Id + username: + type: string + title: Username + email: + type: string + title: Email + first_name: + anyOf: + - type: string + - type: 'null' + title: First Name + last_name: + anyOf: + - type: string + - type: 'null' + title: Last Name + is_active: + type: boolean + title: Is Active + is_verified: + type: boolean + title: Is Verified + created_at: + type: string + format: date-time + title: Created At + type: object + required: + - id + - username + - email + - first_name + - last_name + - is_active + - is_verified + - created_at + title: UserResponse + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + input: + title: Input + ctx: + type: object + title: Context + type: object + required: + - loc + - msg + - type + title: ValidationError + ValidationRequest: + properties: + site_name: + type: string + title: Site Name + description: Name of the site to validate against + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Credentials to validate + type: object + required: + - site_name + - credentials + title: ValidationRequest + description: Request model for credential validation + example: + credentials: + email: john.doe@example.com + password: SecurePass123! + username: johndoe + site_name: github + ValidationResult: + properties: + valid: + type: boolean + title: Valid + description: Whether credentials are valid + missing_fields: + items: + type: string + type: array + title: Missing Fields + description: List of required fields that are missing + invalid_fields: + items: + type: string + type: array + title: Invalid Fields + description: List of fields with invalid values + warnings: + items: + type: string + type: array + title: Warnings + description: List of warnings about the credentials + type: object + required: + - valid + - missing_fields + - invalid_fields + - warnings + title: ValidationResult + description: Response model for credential validation + example: + invalid_fields: [] + missing_fields: [] + valid: true + warnings: [] + VerificationResponse: + properties: + request_id: + type: string + title: Request Id + status: + type: string + title: Status + code: + anyOf: + - type: string + - type: 'null' + title: Code + timestamp: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Timestamp + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + type: object + required: + - request_id + - status + title: VerificationResponse + app__routers__chat__SignupRequest: + properties: + website_url: + type: string + title: Website Url + identity_id: + type: integer + title: Identity Id + additional_instructions: + anyOf: + - type: string + - type: 'null' + title: Additional Instructions + type: object + required: + - website_url + - identity_id + title: SignupRequest + app__routers__site_integrations__SignupRequest: + properties: + site: + type: string + title: Site + email: + type: string + format: email + title: Email + password: + type: string + title: Password + first_name: + type: string + title: First Name + last_name: + type: string + title: Last Name + company_name: + anyOf: + - type: string + - type: 'null' + title: Company Name + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + job_title: + anyOf: + - type: string + - type: 'null' + title: Job Title + headless: + type: boolean + title: Headless + default: true + type: object + required: + - site + - email + - password + - first_name + - last_name + title: SignupRequest + securitySchemes: + HTTPBearer: + type: http + scheme: bearer +tags: +- name: Sites Management + description: Operations for managing automation target sites +- name: Authentication + description: User authentication and authorization +- name: Identities + description: Digital identity management +- name: Accounts + description: Account creation and management +- name: Automation + description: Automation script execution +- name: Chat + description: AI chat interface +- name: SMS + description: SMS and mobile integration +- name: Infrastructure + description: Infrastructure management +- name: LLM Scraper + description: AI-powered scraper generation +- name: Credentials + description: Secure credential management +- name: Google Integration + description: Google services integration +- name: Site Integrations + description: Website integration management +- name: Demo + description: Demo endpoints for testing diff --git a/deploy/argocd/README.md b/deploy/argocd/README.md new file mode 100644 index 0000000..7fdb3dd --- /dev/null +++ b/deploy/argocd/README.md @@ -0,0 +1,62 @@ +# Argo CD adoption spec — FuzeKeys + +**FuzeInfra owns Argo `Application` and `AppProject` objects.** They are the +destination and security boundary for the shared cluster, so a product repo that +ships its own competes with that boundary. This directory therefore must not gain +any NEW Application manifest, and the spec below — not a YAML file here — is how +FuzeInfra adopts changes to this release. + +> **Pre-existing, and NOT changed by this PR.** `applications/fuzekeys-platform.yaml`, +> `applications/fuzekeys-sealed.yaml`, `app-of-apps.yaml` and `project.yaml` are +> still in this repo from before the family moved Application ownership to +> FuzeInfra (FuzeMarket PR #61 did that move for FuzeMarket). Deleting them is a +> deploy-affecting change owned by that workstream, not by this one, so they are +> flagged here rather than removed. When they go, this document is what replaces +> them. + +## The spec FuzeInfra needs + +| Field | Value | +|---|---| +| `spec.source.repoURL` | `https://github.com/izzywdev/FuzeKeys.git` | +| `spec.source.targetRevision` | `master` | +| `spec.source.path` | `deploy/helm/fuzekeys` | +| `spec.source.helm.releaseName` | `fuzekeys` | +| `spec.source.helm.valueFiles` | `values-contabo.yaml` (layers on the chart's own `values.yaml`) | +| `spec.destination.server` | `https://kubernetes.default.svc` | +| `spec.destination.namespace` | `fuzekeys` | +| `spec.project` | FuzeInfra's decision | +| `spec.syncPolicy.automated` | `prune: true`, `selfHeal: true` | +| `spec.syncPolicy.syncOptions` | `CreateNamespace=true`, `ServerSideApply=true` | + +`ServerSideApply=true` matters here: the chart mounts a ~110 KB OpenAPI document +into a ConfigMap, which exceeds what client-side apply can carry in the +`last-applied-configuration` annotation. + +## What the release contains + +| Workload | Enabled by | Notes | +|---|---|---| +| `fuzekeys-backend` (Deployment + Service, :8002) | `backend.enabled` — **true** | FastAPI. Serves `GET /health` and publishes its own OpenAPI at `/openapi.json` and `/docs`. | +| `fuzekeys-frontend` (Deployment + Service) | `frontend.enabled` — **true** | React SPA on nginx | +| `fuzekeys-migrate` (Job) | `migrate.enabled` | `alembic upgrade head`, pre-sync | +| `fuzekeys-mcp` (Deployment + Service, :8081) | `mcp.enabled` — **true** in `values-contabo.yaml` | the shared `@fuzefront/mcp-gateway` image, pointed at `fuzekeys-backend:8002` | +| `fuzekeys-a2a` (Deployment + Service) | `a2a.enabled` — **false**, everywhere | blocked; preconditions are in `deploy/helm/fuzekeys/templates/a2a.yaml` | +| pii-tokenizer stack (tokenizer, presidio, litellm, vault, ollama, redis) | all **false** in `values-contabo.yaml` | gated off pending the Vault auto-unseal design | + +The backend IS deployed, so the MCP pod has a real upstream to call. (That is +worth stating explicitly: a sibling in this family ships a chart that deploys +only its MFE, which leaves its MCP pod able to enumerate tools and unable to +execute any of them.) + +## Secrets it expects to already exist in the namespace + +| Secret | Provided by | Used for | +|---|---|---| +| `fuzekeys-secrets` (`secrets.existingSecret`) | `deploy/argocd/sealed/fuzekeys-secrets.yaml` | DB URL, encryption keys, API keys | +| `fuzekeys-tokenizer-secrets` | `deploy/argocd/sealed/` | only when the tokenizer stack is enabled | +| `ghcr-pull-secret` | `deploy/argocd/sealed/` | image pulls | + +The MCP gateway pod needs **no** Secret — it holds no credential of its own and +forwards the caller's `Authorization` header, which on a credential vault is the +entire point. diff --git a/deploy/helm/fuzekeys/files/openapi.yaml b/deploy/helm/fuzekeys/files/openapi.yaml new file mode 100644 index 0000000..8ae6e3d --- /dev/null +++ b/deploy/helm/fuzekeys/files/openapi.yaml @@ -0,0 +1,4067 @@ +# GENERATED — do not hand-edit. +# Source of truth: the FastAPI application (backend/app/main.py). +# Regenerate: python scripts/export-openapi.py +# Verify: python scripts/export-openapi.py --check +openapi: 3.1.0 +info: + title: FuzeKeys API + description: 'FuzeKeys — identity and credential vault. Managed digital identities, per-site accounts, + encrypted vault assets, capability-token secret brokering, and automated signup workflows. + + + DERIVED DOCUMENT: regenerate with `python scripts/export-openapi.py`. Do not hand-edit.' + license: + name: MIT + url: https://opensource.org/licenses/MIT + version: 2.0.0 +paths: + /api/v1/auth/register: + post: + tags: + - Authentication + summary: Register + description: Register a new user. + operationId: post_auth_register + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserCreate' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UserResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/auth/login: + post: + tags: + - Authentication + summary: Login + description: Authenticate user and return access token. + operationId: post_auth_login + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserLogin' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Token' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/auth/me: + get: + tags: + - Authentication + summary: Get Current User Info + description: Get current user information. + operationId: get_auth_me + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UserResponse' + security: + - HTTPBearer: [] + /api/v1/auth/logout: + post: + tags: + - Authentication + summary: Logout + description: Logout user (client should discard token). + operationId: post_auth_logout + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/identities/: + get: + tags: + - Identities + summary: List Identities + description: List all identities for the current user. + operationId: get_identities + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/IdentityListResponse' + type: array + title: Response List Identities Api V1 Identities Get + security: + - HTTPBearer: [] + post: + tags: + - Identities + summary: Create Identity + description: Create a new identity. + operationId: post_identities + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityCreate' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/identities/{identity_id}: + get: + tags: + - Identities + summary: Get Identity + description: Get a specific identity. + operationId: get_identities_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + put: + tags: + - Identities + summary: Update Identity + description: Update an identity. + operationId: put_identities_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - Identities + summary: Delete Identity + description: Delete an identity. + operationId: delete_identities_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/accounts/: + get: + tags: + - Accounts + summary: List Accounts + description: List all accounts for the current user with stage information. + operationId: get_accounts + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/AccountResponse' + type: array + title: Response List Accounts Api V1 Accounts Get + security: + - HTTPBearer: [] + post: + tags: + - Accounts + summary: Create Account + description: Create a new account with stages. + operationId: post_accounts + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCreate' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/accounts/{account_id}/stages/{stage_id}: + patch: + tags: + - Accounts + summary: Update Account Stage + description: Update a specific account stage. + operationId: patch_accounts_by_account_id_stages_by_stage_id + security: + - HTTPBearer: [] + parameters: + - name: account_id + in: path + required: true + schema: + type: integer + title: Account Id + - name: stage_id + in: path + required: true + schema: + type: integer + title: Stage Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountStageUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/automation/analyze: + post: + tags: + - Automation + summary: Analyze Website + description: Analyze a website's signup process. + operationId: post_automation_analyze + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeWebsiteRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AnalysisResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/chat/message: + post: + tags: + - Chat + summary: Chat Message + description: Process chat message and return response. + operationId: post_chat_message + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ChatMessage' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ChatResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/chat/signup: + post: + tags: + - Chat + summary: Initiate Signup + description: Initiate automated signup process. + operationId: post_chat_signup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/app__routers__chat__SignupRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ChatResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/sms/register-device: + post: + tags: + - SMS + - SMS + summary: Register Device + description: 'Register a new SMS interceptor device. + + + SECURITY / BOOTSTRAP TRUST ASSUMPTION: This endpoint is intentionally left + + UNauthenticated because it is the bootstrap that ISSUES the per-device API + + key — requiring the device key here would be a chicken-and-egg problem and + + break first-time registration. The trust assumption is therefore that + + registration is reachable only by intended devices (network/ingress policy + + or an out-of-band enrolment secret should gate it in production). Note it is + + also self-overwriting: re-registering an existing device_id rotates its key, + + so exposure should be limited by the surrounding network controls. + + PRODUCTION NOTE: add an enrolment token / mutual-TLS / signed attestation so + + arbitrary callers cannot register or hijack a device_id.' + operationId: post_sms_register_device + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DeviceRegistrationRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/otp: + post: + tags: + - SMS + - SMS + summary: Receive Otp + description: 'Receive OTP code from mobile app. + + + SECURITY: This endpoint accepts device-submitted data and is therefore + + authenticated. The submitting device must present the API key it was + + issued at registration (via the ``X-Device-Key`` header) and must specify + + which pending request it is fulfilling. The device is then verified to be + + the device that owns/was assigned that request. This closes the OTP + + hijacking hole where any unauthenticated caller could complete the + + "first waiting" request with an attacker-controlled OTP.' + operationId: post_sms_otp + parameters: + - name: X-Device-Key + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Device-Key + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OtpRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/requests/{device_id}: + get: + tags: + - SMS + - SMS + summary: Get Otp Requests + description: 'Get pending OTP requests for a device. + + + SECURITY: This is a DEVICE-to-server callback (the mobile interceptor polls + + for work it should fulfil), so it is authenticated with the per-device API + + key exactly like ``/otp`` — not the user JWT. The device must present the + + ``X-Device-Key`` it was issued at registration, and that key must belong to + + the ``device_id`` in the path, preventing one device from enumerating + + another device''s pending requests. Unknown/unauthenticated devices get 401.' + operationId: get_sms_requests_by_device_id + parameters: + - name: device_id + in: path + required: true + schema: + type: string + title: Device Id + - name: X-Device-Key + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Device-Key + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/request-otp: + post: + tags: + - SMS + - SMS + summary: Request Otp + description: 'Request an OTP for a specific service (called by your main app). + + + SECURITY: This is an OPERATOR/USER-facing action (the main app asks the + + platform to wait for an OTP for a given service), so it requires the + + application JWT via get_current_user. An unauthenticated caller could + + otherwise spam OTP requests / push fake jobs to devices. Devices do not + + call this endpoint, so device-key auth is not appropriate here.' + operationId: post_sms_request_otp + security: + - HTTPBearer: [] + parameters: + - name: service + in: query + required: true + schema: + type: string + title: Service + - name: timeout_seconds + in: query + required: false + schema: + type: integer + default: 300 + title: Timeout Seconds + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/request-status/{request_id}: + get: + tags: + - SMS + - SMS + summary: Get Request Status + description: 'Get the status of an OTP request. + + + SECURITY: This endpoint returns the received OTP value itself + + (``otp_code``), which is highly sensitive. It is operator/user-facing + + (the main app polls for the result), so it requires the application JWT. + + Leaving it unauthenticated would let any caller read OTP codes by guessing + + or enumerating request_ids. Device-key auth is not used because devices + + submit OTPs (via /otp), they do not read them back.' + operationId: get_sms_request_status_by_request_id + security: + - HTTPBearer: [] + parameters: + - name: request_id + in: path + required: true + schema: + type: string + title: Request Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/sms/devices: + get: + tags: + - SMS + - SMS + summary: Get Devices + description: 'Get all registered SMS devices. + + + SECURITY: This lists the full device inventory (ids, names, OS/app + + versions, activity) which is sensitive operational/PII-ish data and an + + enumeration aid for attackers. It is an operator/admin view, so it requires + + the application JWT. Not a device callback, so device-key auth does not fit.' + operationId: get_sms_devices + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + security: + - HTTPBearer: [] + /api/sms/health: + get: + tags: + - SMS + - SMS + summary: Health Check + description: 'Health check endpoint. + + + SECURITY: Left unauthenticated by design — health/liveness probes are + + called by infrastructure (load balancers, k8s) before any auth context + + exists. It returns only coarse counts (active connections, pending request + + count), not OTP values, device ids, or other sensitive data, so no auth is + + required.' + operationId: get_sms_health + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/infrastructure/sms/request-verification: + post: + tags: + - Infrastructure + - Infrastructure + summary: Request Sms Verification + description: 'Request SMS verification from mobile device for scraper use. + + + SECURITY: Operator/app-facing — this initiates a verification job and + + broadcasts it to mobile devices. Requires the application JWT so arbitrary + + callers cannot push fake jobs to devices or exhaust resources. The + + request->device binding is established later, when a device authenticates + + and submits the code via /sms/complete-verification.' + operationId: post_infrastructure_sms_request_verification + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SmsVerificationRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + type: string + type: object + title: Response Request Sms Verification Api Infrastructure Sms Request Verification Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/infrastructure/sms/get-verification/{request_id}: + get: + tags: + - Infrastructure + - Infrastructure + summary: Get Sms Verification + description: 'Get SMS verification code for scraper. + + + SECURITY: Returns the verification CODE itself, which is highly sensitive. + + Operator/app-facing (the scraper orchestration polls for the result via the + + app), so it requires the application JWT. Previously unauthenticated, this + + let any caller read codes by enumerating request_ids.' + operationId: get_infrastructure_sms_get_verification_by_request_id + security: + - HTTPBearer: [] + parameters: + - name: request_id + in: path + required: true + schema: + type: string + title: Request Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/sms/complete-verification/{request_id}: + post: + tags: + - Infrastructure + - Infrastructure + summary: Complete Sms Verification + description: "Called by mobile device to complete SMS verification.\n\nSECURITY: This is a DEVICE-to-server\ + \ callback that submits an\nattacker-influenceable value (the verification ``code``). It is therefore\n\ + authenticated with the SAME per-device API key model as sms.py:\n\n1) AUTH — the submitting device\ + \ must present the ``X-Device-Key`` it was\n issued at registration, verified against ``device_id``\ + \ via the shared\n ``_verify_device``. Unknown/unauthenticated devices are rejected 401.\n \ + \ This closes the hole where ANY unauthenticated caller could complete a\n verification with\ + \ an attacker-controlled code.\n2) BINDING — the request is bound to a specific device. infrastructure.py\n\ + \ owns its own request store (``verification_requests``), and that store\n had no device assignment\ + \ at request time, so (mirroring sms.py's /otp)\n we bind on first authenticated completion:\ + \ the first authenticated\n device to answer becomes the assigned device, and a DIFFERENT device\n\ + \ attempting to complete the same request is rejected 403. This prevents\n a second (even\ + \ authenticated) device from overwriting another device's\n in-flight verification." + operationId: post_infrastructure_sms_complete_verification_by_request_id + parameters: + - name: request_id + in: path + required: true + schema: + type: string + title: Request Id + - name: code + in: query + required: true + schema: + type: string + title: Code + - name: device_id + in: query + required: true + schema: + type: string + title: Device Id + - name: X-Device-Key + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Device-Key + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/email/setup-monitoring: + post: + tags: + - Infrastructure + - Infrastructure + summary: Setup Email Monitoring + description: 'Setup email monitoring for verification emails. + + + SECURITY: Operator/app-facing — it registers monitoring on an email + + address and patterns (sensitive targeting data). Requires the application + + JWT; not a device callback.' + operationId: post_infrastructure_email_setup_monitoring + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmailMonitoringRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + type: string + type: object + title: Response Setup Email Monitoring Api Infrastructure Email Setup Monitoring Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/infrastructure/email/get-verification/{monitor_id}: + get: + tags: + - Infrastructure + - Infrastructure + summary: Get Email Verification + description: 'Get email verification content. + + + SECURITY: Returns captured email content (``found_emails``), which is + + sensitive. Operator/app-facing, so it requires the application JWT. + + Previously unauthenticated, allowing enumeration of monitor_ids to read + + intercepted emails.' + operationId: get_infrastructure_email_get_verification_by_monitor_id + security: + - HTTPBearer: [] + parameters: + - name: monitor_id + in: path + required: true + schema: + type: string + title: Monitor Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/mobile/send-command: + post: + tags: + - Infrastructure + - Infrastructure + summary: Send Mobile Command + description: 'Send command to mobile device for UI automation. + + + SECURITY: Operator/app-facing — this dispatches automation commands to + + devices. Requires the application JWT so an unauthenticated caller cannot + + drive devices / inject commands.' + operationId: post_infrastructure_mobile_send_command + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MobileCommandRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + type: string + type: object + title: Response Send Mobile Command Api Infrastructure Mobile Send Command Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/infrastructure/mobile/get-command-result/{command_id}: + get: + tags: + - Infrastructure + - Infrastructure + summary: Get Mobile Command Result + description: 'Get result of mobile command execution. + + + SECURITY: Operator/app-facing. Although currently a stub, it is designed to + + surface results returned by mobile devices (potentially sensitive), so it + + is gated by the application JWT now to avoid shipping an unauthenticated + + read endpoint once result tracking is implemented.' + operationId: get_infrastructure_mobile_get_command_result_by_command_id + security: + - HTTPBearer: [] + parameters: + - name: command_id + in: path + required: true + schema: + type: string + title: Command Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/scraper/report-error: + post: + tags: + - Infrastructure + - Infrastructure + summary: Report Scraper Error + description: 'Report scraper execution error for analysis. + + + SECURITY: Accepts arbitrary external input (scraper_id + free-form data). + + Scrapers run under the platform/operator identity, so this requires the + + application JWT to prevent unauthenticated log/data injection and spam. + + Not a mobile-device callback, so device-key auth does not apply.' + operationId: post_infrastructure_scraper_report_error + security: + - HTTPBearer: [] + parameters: + - name: scraper_id + in: query + required: true + schema: + type: string + title: Scraper Id + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + title: Error Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/infrastructure/scraper/report-success: + post: + tags: + - Infrastructure + - Infrastructure + summary: Report Scraper Success + description: 'Report scraper execution success. + + + SECURITY: Accepts arbitrary external input; gated by the application JWT for + + the same reasons as /scraper/report-error (the scraper acts as the + + platform/operator). Not a device callback.' + operationId: post_infrastructure_scraper_report_success + security: + - HTTPBearer: [] + parameters: + - name: scraper_id + in: query + required: true + schema: + type: string + title: Scraper Id + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + title: Success Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/llm-scraper/generate: + post: + tags: + - LLM Scraper + - LLM Scraper + summary: Generate Scraper + description: Generate a new scraper for a site + operationId: post_llm_scraper_generate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GenerateScraperRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/llm-scraper/improve: + post: + tags: + - LLM Scraper + - LLM Scraper + summary: Improve Scraper + description: Improve an existing scraper based on execution results + operationId: post_llm_scraper_improve + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImproveScraperRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/llm-scraper/scrapers: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: List Scrapers + description: List all generated scrapers + operationId: get_llm_scraper_scrapers + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperListResponse' + security: + - HTTPBearer: [] + /api/llm-scraper/scrapers/{site_name}/{action_type}: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: Get Scraper + description: Get a specific scraper + operationId: get_llm_scraper_scrapers_by_site_name_by_action_type + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + - name: action_type + in: path + required: true + schema: + type: string + title: Action Type + - name: version + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Version + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScraperResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - LLM Scraper + - LLM Scraper + summary: Delete Scraper + description: Delete a scraper and all its versions + operationId: delete_llm_scraper_scrapers_by_site_name_by_action_type + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + - name: action_type + in: path + required: true + schema: + type: string + title: Action Type + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/llm-scraper/scrapers/{site_name}/{action_type}/history: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: Get Scraper History + description: Get the full history of a scraper + operationId: get_llm_scraper_scrapers_by_site_name_by_action_type_history + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + - name: action_type + in: path + required: true + schema: + type: string + title: Action Type + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/llm-scraper/stats: + get: + tags: + - LLM Scraper + - LLM Scraper + summary: Get Generation Stats + description: Get statistics about scraper generation + operationId: get_llm_scraper_stats + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + security: + - HTTPBearer: [] + /api/llm-scraper/debug: + post: + tags: + - LLM Scraper + - LLM Scraper + summary: Debug Scraper + description: Debug a specific scraper issue + operationId: post_llm_scraper_debug + security: + - HTTPBearer: [] + parameters: + - name: site_name + in: query + required: true + schema: + type: string + title: Site Name + - name: action_type + in: query + required: true + schema: + type: string + title: Action Type + - name: error + in: query + required: true + schema: + type: string + title: Error + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + title: Context + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/request-identity-credentials: + post: + tags: + - Credentials + - Credentials + summary: 🔐 Generate Credentials for Identity + description: 'Generate fresh credentials for an identity to sign up for a specific site. + + + This endpoint creates site-appropriate credentials based on the identity''s information + + and the target website''s requirements. Perfect for automated signup processes. + + + **Use Cases:** + + - Generate signup credentials for new accounts + + - Create site-specific usernames and emails + + - Get credentials for scraper automation + + + **Security:** All requests are logged with service identity for audit purposes.' + operationId: post_credentials_request_identity_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialRequest' + responses: + '200': + description: Credentials generated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialResponse' + '401': + description: Invalid API key + '404': + description: Identity not found + '500': + description: Failed to generate credentials + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/request-account-credentials: + post: + tags: + - Credentials + - Credentials + summary: 🔑 Retrieve Stored Account Credentials + description: 'Retrieve stored credentials for an existing account. + + + Perfect for accessing previously created accounts where credentials have been + + stored after successful signup. + + + **Use Cases:** + + - Get credentials for existing account signin + + - Retrieve API keys for service integration + + - Access 2FA backup codes + + + **Security:** Updates last accessed timestamp and logs access for audit.' + operationId: post_credentials_request_account_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCredentialRequest' + responses: + '200': + description: Credentials retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCredentialResponse' + '401': + description: Invalid API key + '404': + description: Account not found + '500': + description: Failed to retrieve credentials + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/store-account-credentials: + post: + tags: + - Credentials + - Credentials + summary: 💾 Store Account Credentials + description: 'Store or update credentials for an account after successful signup. + + + Use this endpoint to securely store credentials after a successful account + + creation process. All credentials are encrypted before storage. + + + **Use Cases:** + + - Store credentials after successful signup + + - Update credentials with new API keys + + - Save 2FA backup codes and recovery information + + + **Security:** All credentials are encrypted using Fernet (AES 128) before storage.' + operationId: post_credentials_store_account_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialUpdate' + responses: + '200': + description: Credentials stored successfully + content: + application/json: + schema: {} + '401': + description: Invalid API key + '404': + description: Account not found + '500': + description: Failed to store credentials + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/account/{account_id}/credentials: + get: + tags: + - Credentials + - Credentials + summary: 📋 Get Account Credentials (GET) + description: 'Alternative GET endpoint for retrieving account credentials. + + + Convenient endpoint for getting credentials when you know the account ID.' + operationId: get_credentials_account_by_account_id_credentials + parameters: + - name: account_id + in: path + required: true + schema: + type: integer + description: Account ID to retrieve credentials for + title: Account Id + description: Account ID to retrieve credentials for + - name: identity_id + in: query + required: true + schema: + type: integer + description: ID of the identity that owns the account + title: Identity Id + description: ID of the identity that owns the account + - name: credential_types + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Comma-separated list of credential types to retrieve + title: Credential Types + description: Comma-separated list of credential types to retrieve + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCredentialResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/identity/{identity_id}/accounts: + get: + tags: + - Credentials + - Credentials + summary: 👤 List Identity Accounts + description: 'Get all accounts associated with a specific identity. + + + Useful for understanding what accounts an identity has created and their status.' + operationId: get_credentials_identity_by_identity_id_accounts + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + description: Identity ID to get accounts for + title: Identity Id + description: Identity ID to get accounts for + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/validate-credentials: + post: + tags: + - Credentials + - Credentials + summary: ✅ Validate Credential Format + description: 'Validate credentials format for a specific site. + + + Check if credentials meet the requirements for a particular website before + + attempting to use them. + + + **Validation includes:** + + - Required field presence + + - Email format validation + + - Password strength requirements + + - Site-specific username patterns' + operationId: post_credentials_validate_credentials + parameters: + - name: x-api-key + in: header + required: true + schema: + type: string + description: API key for service authentication + title: X-Api-Key + description: API key for service authentication + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationResult' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/credentials/health: + get: + tags: + - Credentials + - Credentials + summary: 🏥 Health Check + description: Check the health status of the credentials service + operationId: get_credentials_health + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/google/signup/{identity_id}: + post: + tags: + - Google Integration + - Google Integration + summary: Signup With Identity + description: 'Create a Google account using the specified identity. + + + SECURITY: requires auth and that the identity is owned by the caller.' + operationId: post_google_signup_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GoogleSignupConfig' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Signup With Identity Api Google Signup Identity Id Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/google/signup/manual: + post: + tags: + - Google Integration + - Google Integration + summary: Manual Signup + description: 'Create a Google account with manually provided data. + + + SECURITY: requires auth. This route drives real signup automation from the + + request body and must not be reachable unauthenticated.' + operationId: post_google_signup_manual + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Body_manual_signup_api_google_signup_manual_post' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response Manual Signup Api Google Signup Manual Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/google/config/default: + get: + tags: + - Google Integration + - Google Integration + summary: Get Default Config + description: 'Get the default configuration for Google signup. + + + SECURITY: requires auth. Even though it returns no PII, keep the whole + + router authenticated so there is no unauthenticated surface here.' + operationId: get_google_config_default + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GoogleSignupConfig' + security: + - HTTPBearer: [] + /api/google/test/identity-conversion/{identity_id}: + post: + tags: + - Google Integration + - Google Integration + summary: Test Identity Conversion + description: 'Test converting an identity to Google signup data without actually creating an account. + + + SECURITY (CRITICAL-1): this endpoint returns DECRYPTED PII (names, username, + + phone, recovery email, birth date, gender). It now requires auth and that the + + identity belongs to the caller; otherwise 404. This closes the unauthenticated + + PII-exfiltration path.' + operationId: post_google_test_identity_conversion_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Test Identity Conversion Api Google Test Identity Conversion Identity + Id Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/google/accounts/{identity_id}: + get: + tags: + - Google Integration + - Google Integration + summary: Get Google Accounts + description: 'Get all Google accounts for a specific identity. + + + SECURITY (CRITICAL-1): requires auth; the identity must be owned by the + + caller (404 otherwise). The account lookup is additionally scoped through the + + owned identity so no cross-tenant account data is returned.' + operationId: get_google_accounts_by_identity_id + security: + - HTTPBearer: [] + parameters: + - name: identity_id + in: path + required: true + schema: + type: integer + title: Identity Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Google Accounts Api Google Accounts Identity Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/integrations/sites: + get: + tags: + - Site Integrations + - Site Integrations + summary: List Available Sites + description: Get a list of all available site integrations. + operationId: get_integrations_sites + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + items: + type: string + type: array + type: object + title: Response List Available Sites Api V1 Integrations Sites Get + /api/v1/integrations/sites/{site_name}/capabilities: + get: + tags: + - Site Integrations + - Site Integrations + summary: Get Site Capabilities Endpoint + description: Get the capabilities of a specific site integration. + operationId: get_integrations_sites_by_site_name_capabilities + parameters: + - name: site_name + in: path + required: true + schema: + type: string + title: Site Name + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/integrations/signup: + post: + tags: + - Site Integrations + - Site Integrations + summary: Create Account + description: 'Create a new account on the specified site. + + + This endpoint handles automated account signup for supported sites. + + The operation runs in the background for long-running automations. + + + SECURITY (HIGH-1 / appsec #18): requires auth. This drives credential-bearing + + browser automation against external sites; an unauthenticated caller must not + + be able to trigger it (abuse / resource exhaustion).' + operationId: post_integrations_signup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/app__routers__site_integrations__SignupRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IntegrationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/integrations/signin: + post: + tags: + - Site Integrations + - Site Integrations + summary: Authenticate Account + description: 'Authenticate with an existing account on the specified site. + + + This endpoint handles automated authentication for supported sites. + + + SECURITY (HIGH-1 / appsec #18): requires auth. This accepts raw email+password + + and drives credential-bearing browser automation against external sites; an + + unauthenticated caller must not be able to trigger it (abuse / resource + + exhaustion / credential-stuffing surface).' + operationId: post_integrations_signin + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SigninRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IntegrationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/integrations/apikey: + post: + tags: + - Site Integrations + - Site Integrations + summary: Create Api Key + description: 'Create an API key for the specified site account. + + + This endpoint handles automated API key creation for supported sites. + + + SECURITY (HIGH-1 / appsec #18): requires auth. This accepts raw email+password + + and drives credential-bearing browser automation against external sites; an + + unauthenticated caller must not be able to trigger it.' + operationId: post_integrations_apikey + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKeyRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IntegrationResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] + /api/v1/integrations/health: + get: + tags: + - Site Integrations + - Site Integrations + summary: Integration Health Check + description: Health check endpoint for site integrations. + operationId: get_integrations_health + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/broker/grant: + post: + tags: + - Secret Broker + - Secret Broker + summary: Grant + description: Issue a grant. The grantor is the caller's authenticated transport identity. + operationId: post_broker_grant + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GrantRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/broker/redeem: + post: + tags: + - Secret Broker + - Secret Broker + summary: Redeem + operationId: post_broker_redeem + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RedeemRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/broker/mint-token: + post: + tags: + - Secret Broker + - Secret Broker + summary: Mint Token + operationId: post_broker_mint_token + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MintTokenRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/broker/revoke: + post: + tags: + - Secret Broker + - Secret Broker + summary: Revoke + operationId: post_broker_revoke + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RevokeRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/sites/categories: + get: + tags: + - Sites Management + - Sites Management + summary: List Categories + description: Get list of all categories. + operationId: get_sites_categories + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/sites/stats/overview: + get: + tags: + - Sites Management + - Sites Management + summary: Get Sites Overview + description: Get overview statistics of sites. + operationId: get_sites_stats_overview + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/sites/: + get: + tags: + - Sites Management + - Sites Management + summary: List Sites + description: Get list of sites with filtering and pagination. + operationId: get_sites + parameters: + - name: skip + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: Skip + - name: limit + in: query + required: false + schema: + type: integer + maximum: 1000 + minimum: 1 + default: 100 + title: Limit + - name: category + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Category + - name: difficulty + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Difficulty + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + - name: priority_min + in: query + required: false + schema: + anyOf: + - type: integer + maximum: 100 + minimum: 1 + - type: 'null' + title: Priority Min + - name: search + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Search + - name: sort_by + in: query + required: false + schema: + type: string + pattern: ^(name|priority|difficulty|progress|created_at)$ + default: priority + title: Sort By + - name: sort_order + in: query + required: false + schema: + type: string + pattern: ^(asc|desc)$ + default: desc + title: Sort Order + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SiteResponse' + title: Response List Sites Api V1 Sites Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/sites/{site_id}: + get: + tags: + - Sites Management + - Sites Management + summary: Get Site + description: Get specific site by ID. + operationId: get_sites_by_site_id + parameters: + - name: site_id + in: path + required: true + schema: + type: integer + title: Site Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/SiteResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /: + get: + summary: API Health Check + description: Root endpoint that returns API status and basic information + operationId: get_root + responses: + '200': + description: API status and version information + content: + application/json: + schema: {} + /health: + get: + summary: Detailed Health Check + description: Comprehensive health check with service status details + operationId: get_health + responses: + '200': + description: Detailed health status of all services + content: + application/json: + schema: {} + /api/v1/demo/identities: + get: + tags: + - Demo + summary: Demo Identities + description: Sample identity data for testing and demonstration + operationId: get_demo_identities + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/demo/accounts: + get: + tags: + - Demo + summary: Demo Accounts + description: Sample account data for testing and demonstration + operationId: get_demo_accounts + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/demo/chat: + post: + tags: + - Demo + summary: Demo Chat + description: Demo AI chat interface for testing conversational features + operationId: post_demo_chat + requestBody: + content: + application/json: + schema: + additionalProperties: true + type: object + title: Message + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' +components: + schemas: + AccountCreate: + properties: + website_name: + type: string + title: Website Name + website_url: + type: string + title: Website Url + identity_id: + type: integer + title: Identity Id + website_domain: + anyOf: + - type: string + - type: 'null' + title: Website Domain + account_type: + anyOf: + - type: string + - type: 'null' + title: Account Type + default: free + signup_method: + anyOf: + - type: string + - type: 'null' + title: Signup Method + default: automated + stages: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Stages + type: object + required: + - website_name + - website_url + - identity_id + title: AccountCreate + AccountCredentialRequest: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity that owns the account + example: 1 + account_id: + type: integer + title: Account Id + description: ID of the account to retrieve credentials for + example: 1 + credential_types: + items: + type: string + type: array + title: Credential Types + description: List of credential types to retrieve + example: + - email + - password + type: object + required: + - identity_id + - account_id + - credential_types + title: AccountCredentialRequest + description: Request model for retrieving stored account credentials + example: + account_id: 1 + credential_types: + - email + - password + - api_key + identity_id: 1 + AccountCredentialResponse: + properties: + account_id: + type: integer + title: Account Id + description: ID of the account + site_name: + type: string + title: Site Name + description: Name of the website + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Stored credentials + last_used: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Last Used + description: Timestamp when credentials were last accessed + type: object + required: + - account_id + - site_name + - credentials + title: AccountCredentialResponse + description: Response model for stored account credentials + example: + account_id: 1 + credentials: + api_key: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + email: john.doe.1703123456@example.com + password: JohnDoePass123! + last_used: '2024-01-01T12:00:00Z' + site_name: github + AccountResponse: + properties: + id: + type: integer + title: Id + website_name: + type: string + title: Website Name + website_url: + type: string + title: Website Url + identity_id: + type: integer + title: Identity Id + identity_name: + type: string + title: Identity Name + is_active: + type: boolean + title: Is Active + signup_completed: + type: boolean + title: Signup Completed + created_at: + type: string + format: date-time + title: Created At + stages: + items: + $ref: '#/components/schemas/StageStatusResponse' + type: array + title: Stages + type: object + required: + - id + - website_name + - website_url + - identity_id + - identity_name + - is_active + - signup_completed + - created_at + - stages + title: AccountResponse + AccountStageUpdate: + properties: + status: + type: string + title: Status + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + stage_data: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Stage Data + type: object + required: + - status + title: AccountStageUpdate + AnalysisResponse: + properties: + success: + type: boolean + title: Success + message: + type: string + title: Message + details: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Details + type: object + required: + - success + - message + title: AnalysisResponse + AnalyzeWebsiteRequest: + properties: + url: + type: string + title: Url + type: object + required: + - url + title: AnalyzeWebsiteRequest + ApiKeyRequest: + properties: + site: + type: string + title: Site + email: + type: string + format: email + title: Email + password: + type: string + title: Password + key_name: + type: string + title: Key Name + default: FuzeKeys + headless: + type: boolean + title: Headless + default: true + type: object + required: + - site + - email + - password + title: ApiKeyRequest + Body_manual_signup_api_google_signup_manual_post: + properties: + signup_data: + $ref: '#/components/schemas/GoogleSignupData' + config: + $ref: '#/components/schemas/GoogleSignupConfig' + type: object + required: + - signup_data + title: Body_manual_signup_api_google_signup_manual_post + ChatMessage: + properties: + message: + type: string + title: Message + type: object + required: + - message + title: ChatMessage + ChatResponse: + properties: + response: + type: string + title: Response + action_type: + anyOf: + - type: string + - type: 'null' + title: Action Type + suggested_actions: + items: + type: string + type: array + title: Suggested Actions + default: [] + automation_status: + anyOf: + - type: string + - type: 'null' + title: Automation Status + type: object + required: + - response + title: ChatResponse + CredentialRequest: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity to generate credentials for + example: 1 + site_name: + type: string + title: Site Name + description: Name of the target website + example: github + action_type: + type: string + title: Action Type + description: Type of action being performed + example: signup + credential_types: + items: + type: string + type: array + title: Credential Types + description: List of credential types needed + example: + - email + - password + - username + type: object + required: + - identity_id + - site_name + - action_type + - credential_types + title: CredentialRequest + description: Request model for generating credentials for an identity + example: + action_type: signup + credential_types: + - email + - password + - username + - name + identity_id: 1 + site_name: github + CredentialResponse: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity + site_name: + type: string + title: Site Name + description: Name of the target website + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Generated credentials + metadata: + additionalProperties: true + type: object + title: Metadata + description: Request metadata and tracking information + type: object + required: + - identity_id + - site_name + - credentials + - metadata + title: CredentialResponse + description: Response model for generated credentials + example: + credentials: + email: john.doe.1703123456@example.com + name: John Doe + password: JohnDoePass123! + username: johndoe_a1b2 + identity_id: 1 + metadata: + action_type: signup + credential_types: + - email + - password + - username + - name + requested_at: '2024-01-01T12:00:00Z' + requested_by: scraper-service + site_name: github + CredentialUpdate: + properties: + identity_id: + type: integer + title: Identity Id + description: ID of the identity that owns the account + account_id: + type: integer + title: Account Id + description: ID of the account to store credentials for + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Credentials to store + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Metadata + description: Additional metadata about the credentials + type: object + required: + - identity_id + - account_id + - credentials + title: CredentialUpdate + description: Request model for storing/updating account credentials + example: + account_id: 1 + credentials: + api_key: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + email: john.doe.1703123456@example.com + password: JohnDoePass123! + username: johndoe_a1b2 + identity_id: 1 + metadata: + account_type: free + signup_date: '2024-01-01T12:00:00Z' + verification_method: email + DeviceRegistrationRequest: + properties: + device_id: + type: string + title: Device Id + device_name: + type: string + title: Device Name + os_version: + type: string + title: Os Version + app_version: + type: string + title: App Version + type: object + required: + - device_id + - device_name + - os_version + - app_version + title: DeviceRegistrationRequest + EmailMonitoringRequest: + properties: + email: + type: string + title: Email + sender_patterns: + items: + type: string + type: array + title: Sender Patterns + subject_patterns: + items: + type: string + type: array + title: Subject Patterns + timeout_seconds: + type: integer + title: Timeout Seconds + default: 300 + type: object + required: + - email + - sender_patterns + - subject_patterns + title: EmailMonitoringRequest + GenerateScraperRequest: + properties: + site_name: + type: string + title: Site Name + site_url: + type: string + title: Site Url + action_type: + type: string + title: Action Type + patterns: + additionalProperties: true + type: object + title: Patterns + default: {} + test_data: + additionalProperties: true + type: object + title: Test Data + default: {} + type: object + required: + - site_name + - site_url + - action_type + title: GenerateScraperRequest + GoogleSignupConfig: + properties: + use_proxy: + type: boolean + title: Use Proxy + default: false + proxy_config: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Proxy Config + headless: + type: boolean + title: Headless + default: true + timeout: + type: integer + title: Timeout + default: 120 + retry_attempts: + type: integer + title: Retry Attempts + default: 3 + use_mobile_user_agent: + type: boolean + title: Use Mobile User Agent + default: false + custom_user_agent: + anyOf: + - type: string + - type: 'null' + title: Custom User Agent + prefer_phone_verification: + type: boolean + title: Prefer Phone Verification + default: true + auto_handle_captcha: + type: boolean + title: Auto Handle Captcha + default: false + save_cookies: + type: boolean + title: Save Cookies + default: true + type: object + title: GoogleSignupConfig + description: Configuration for Google signup process. + GoogleSignupData: + properties: + first_name: + type: string + maxLength: 50 + minLength: 1 + title: First Name + last_name: + type: string + maxLength: 50 + minLength: 1 + title: Last Name + username: + type: string + maxLength: 30 + minLength: 6 + title: Username + password: + type: string + minLength: 8 + title: Password + phone_number: + anyOf: + - type: string + - type: 'null' + title: Phone Number + description: Phone number for account recovery + recovery_email: + anyOf: + - type: string + - type: 'null' + title: Recovery Email + description: Recovery email address + birth_date: + anyOf: + - type: string + format: date + - type: 'null' + title: Birth Date + description: Date of birth (required for some regions) + gender: + anyOf: + - type: string + - type: 'null' + title: Gender + description: Gender (optional) + interests: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Interests + description: User interests + skip_phone_verification: + type: boolean + title: Skip Phone Verification + description: Try to skip phone verification if possible + default: false + type: object + required: + - first_name + - last_name + - username + - password + title: GoogleSignupData + description: Data structure for Google account signup. + GrantRequest: + properties: + redeemer_identity: + type: string + title: Redeemer Identity + description: Bound transport identity that may redeem + scope: + additionalProperties: true + type: object + title: Scope + ttl_seconds: + anyOf: + - type: integer + - type: 'null' + title: Ttl Seconds + description: Clamped to the server maximum + secret_ref: + anyOf: + - type: string + - type: 'null' + title: Secret Ref + operation: + anyOf: + - type: string + - type: 'null' + title: Operation + single_use: + type: boolean + title: Single Use + default: true + sensitivity: + type: string + title: Sensitivity + default: medium + type: object + required: + - redeemer_identity + title: GrantRequest + HTTPValidationError: + properties: + detail: + items: + $ref: '#/components/schemas/ValidationError' + type: array + title: Detail + type: object + title: HTTPValidationError + IdentityCreate: + properties: + name: + type: string + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + first_name: + type: string + title: First Name + last_name: + type: string + title: Last Name + email: + type: string + format: email + title: Email + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + date_of_birth: + anyOf: + - type: string + - type: 'null' + title: Date Of Birth + address_line1: + anyOf: + - type: string + - type: 'null' + title: Address Line1 + address_line2: + anyOf: + - type: string + - type: 'null' + title: Address Line2 + city: + anyOf: + - type: string + - type: 'null' + title: City + state: + anyOf: + - type: string + - type: 'null' + title: State + zip_code: + anyOf: + - type: string + - type: 'null' + title: Zip Code + country: + anyOf: + - type: string + - type: 'null' + title: Country + profession: + anyOf: + - type: string + - type: 'null' + title: Profession + company: + anyOf: + - type: string + - type: 'null' + title: Company + bio: + anyOf: + - type: string + - type: 'null' + title: Bio + custom_fields: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Custom Fields + preferred_username_pattern: + anyOf: + - type: string + - type: 'null' + title: Preferred Username Pattern + type: object + required: + - name + - first_name + - last_name + - email + title: IdentityCreate + IdentityListResponse: + properties: + id: + type: integer + title: Id + name: + type: string + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + created_at: + type: string + format: date-time + title: Created At + type: object + required: + - id + - name + - description + - created_at + title: IdentityListResponse + IdentityResponse: + properties: + id: + type: integer + title: Id + name: + type: string + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + first_name: + type: string + title: First Name + last_name: + type: string + title: Last Name + email: + type: string + title: Email + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + date_of_birth: + anyOf: + - type: string + - type: 'null' + title: Date Of Birth + address_line1: + anyOf: + - type: string + - type: 'null' + title: Address Line1 + address_line2: + anyOf: + - type: string + - type: 'null' + title: Address Line2 + city: + anyOf: + - type: string + - type: 'null' + title: City + state: + anyOf: + - type: string + - type: 'null' + title: State + zip_code: + anyOf: + - type: string + - type: 'null' + title: Zip Code + country: + anyOf: + - type: string + - type: 'null' + title: Country + profession: + anyOf: + - type: string + - type: 'null' + title: Profession + company: + anyOf: + - type: string + - type: 'null' + title: Company + bio: + anyOf: + - type: string + - type: 'null' + title: Bio + custom_fields: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Custom Fields + preferred_username_pattern: + anyOf: + - type: string + - type: 'null' + title: Preferred Username Pattern + created_at: + type: string + format: date-time + title: Created At + updated_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Updated At + type: object + required: + - id + - name + - description + - first_name + - last_name + - email + - phone + - date_of_birth + - address_line1 + - address_line2 + - city + - state + - zip_code + - country + - profession + - company + - bio + - custom_fields + - preferred_username_pattern + - created_at + - updated_at + title: IdentityResponse + IdentityUpdate: + properties: + name: + anyOf: + - type: string + - type: 'null' + title: Name + description: + anyOf: + - type: string + - type: 'null' + title: Description + first_name: + anyOf: + - type: string + - type: 'null' + title: First Name + last_name: + anyOf: + - type: string + - type: 'null' + title: Last Name + email: + anyOf: + - type: string + format: email + - type: 'null' + title: Email + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + date_of_birth: + anyOf: + - type: string + - type: 'null' + title: Date Of Birth + address_line1: + anyOf: + - type: string + - type: 'null' + title: Address Line1 + address_line2: + anyOf: + - type: string + - type: 'null' + title: Address Line2 + city: + anyOf: + - type: string + - type: 'null' + title: City + state: + anyOf: + - type: string + - type: 'null' + title: State + zip_code: + anyOf: + - type: string + - type: 'null' + title: Zip Code + country: + anyOf: + - type: string + - type: 'null' + title: Country + profession: + anyOf: + - type: string + - type: 'null' + title: Profession + company: + anyOf: + - type: string + - type: 'null' + title: Company + bio: + anyOf: + - type: string + - type: 'null' + title: Bio + custom_fields: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Custom Fields + preferred_username_pattern: + anyOf: + - type: string + - type: 'null' + title: Preferred Username Pattern + type: object + title: IdentityUpdate + ImproveScraperRequest: + properties: + site_name: + type: string + title: Site Name + action_type: + type: string + title: Action Type + execution_result: + additionalProperties: true + type: object + title: Execution Result + type: object + required: + - site_name + - action_type + - execution_result + title: ImproveScraperRequest + IntegrationResponse: + properties: + success: + type: boolean + title: Success + message: + type: string + title: Message + data: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Data + error: + anyOf: + - type: string + - type: 'null' + title: Error + site: + type: string + title: Site + type: object + required: + - success + - message + - site + title: IntegrationResponse + MintTokenRequest: + properties: + audience: + type: string + title: Audience + scope: + type: string + title: Scope + ttl_seconds: + anyOf: + - type: integer + - type: 'null' + title: Ttl Seconds + type: object + required: + - audience + - scope + title: MintTokenRequest + MobileCommandRequest: + properties: + command_type: + type: string + title: Command Type + parameters: + additionalProperties: true + type: object + title: Parameters + timeout_seconds: + type: integer + title: Timeout Seconds + default: 60 + type: object + required: + - command_type + - parameters + title: MobileCommandRequest + OtpRequest: + properties: + otp: + type: string + title: Otp + sender: + type: string + title: Sender + message_body: + type: string + title: Message Body + timestamp: + type: integer + title: Timestamp + device_id: + type: string + title: Device Id + confidence: + anyOf: + - type: number + - type: 'null' + title: Confidence + request_id: + type: string + title: Request Id + type: object + required: + - otp + - sender + - message_body + - timestamp + - device_id + - request_id + title: OtpRequest + RedeemRequest: + properties: + grant_handle: + type: string + title: Grant Handle + type: object + required: + - grant_handle + title: RedeemRequest + RevokeRequest: + properties: + grant_id: + type: string + title: Grant Id + reason: + type: string + title: Reason + default: revoked + type: object + required: + - grant_id + title: RevokeRequest + ScraperListResponse: + properties: + scrapers: + items: + additionalProperties: true + type: object + type: array + title: Scrapers + total_count: + type: integer + title: Total Count + type: object + required: + - scrapers + - total_count + title: ScraperListResponse + ScraperResponse: + properties: + success: + type: boolean + title: Success + scraper_id: + type: string + title: Scraper Id + version: + type: integer + title: Version + code: + anyOf: + - type: string + - type: 'null' + title: Code + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Metadata + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + type: object + required: + - success + - scraper_id + - version + title: ScraperResponse + SigninRequest: + properties: + site: + type: string + title: Site + email: + type: string + format: email + title: Email + password: + type: string + title: Password + headless: + type: boolean + title: Headless + default: true + type: object + required: + - site + - email + - password + title: SigninRequest + SiteResponse: + properties: + id: + type: integer + title: Id + name: + type: string + title: Name + display_name: + type: string + title: Display Name + url: + type: string + title: Url + logo_url: + anyOf: + - type: string + - type: 'null' + title: Logo Url + category: + type: string + title: Category + description: + anyOf: + - type: string + - type: 'null' + title: Description + signup_difficulty: + type: string + title: Signup Difficulty + signin_difficulty: + type: string + title: Signin Difficulty + apikey_difficulty: + type: string + title: Apikey Difficulty + overall_difficulty: + type: string + title: Overall Difficulty + requires_email_verification: + type: boolean + title: Requires Email Verification + requires_phone_verification: + type: boolean + title: Requires Phone Verification + requires_sms_verification: + type: boolean + title: Requires Sms Verification + requires_authenticator: + type: boolean + title: Requires Authenticator + has_captcha: + type: boolean + title: Has Captcha + captcha_type: + anyOf: + - type: string + - type: 'null' + title: Captcha Type + anti_bot_techniques: + items: + type: string + type: array + title: Anti Bot Techniques + signup_status: + type: string + title: Signup Status + signin_status: + type: string + title: Signin Status + apikey_status: + type: string + title: Apikey Status + implementation_progress: + type: number + title: Implementation Progress + priority: + type: integer + title: Priority + estimated_hours: + anyOf: + - type: integer + - type: 'null' + title: Estimated Hours + has_official_api: + type: boolean + title: Has Official Api + api_documentation_url: + anyOf: + - type: string + - type: 'null' + title: Api Documentation Url + api_rate_limits: + anyOf: + - type: string + - type: 'null' + title: Api Rate Limits + notes: + anyOf: + - type: string + - type: 'null' + title: Notes + created_at: + anyOf: + - type: string + - type: 'null' + title: Created At + updated_at: + anyOf: + - type: string + - type: 'null' + title: Updated At + type: object + required: + - id + - name + - display_name + - url + - logo_url + - category + - description + - signup_difficulty + - signin_difficulty + - apikey_difficulty + - overall_difficulty + - requires_email_verification + - requires_phone_verification + - requires_sms_verification + - requires_authenticator + - has_captcha + - captcha_type + - anti_bot_techniques + - signup_status + - signin_status + - apikey_status + - implementation_progress + - priority + - estimated_hours + - has_official_api + - api_documentation_url + - api_rate_limits + - notes + - created_at + - updated_at + title: SiteResponse + SmsVerificationRequest: + properties: + site: + type: string + title: Site + phone_number: + type: string + title: Phone Number + timeout_seconds: + type: integer + title: Timeout Seconds + default: 300 + type: object + required: + - site + - phone_number + title: SmsVerificationRequest + StageStatusResponse: + properties: + stage_type: + type: string + title: Stage Type + stage_name: + type: string + title: Stage Name + status: + type: string + title: Status + attempts: + type: integer + title: Attempts + started_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Started At + completed_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Completed At + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + type: object + required: + - stage_type + - stage_name + - status + - attempts + - started_at + - completed_at + - error_message + title: StageStatusResponse + Token: + properties: + access_token: + type: string + title: Access Token + token_type: + type: string + title: Token Type + type: object + required: + - access_token + - token_type + title: Token + UserCreate: + properties: + username: + type: string + title: Username + email: + type: string + format: email + title: Email + password: + type: string + title: Password + master_key: + type: string + title: Master Key + first_name: + anyOf: + - type: string + - type: 'null' + title: First Name + last_name: + anyOf: + - type: string + - type: 'null' + title: Last Name + type: object + required: + - username + - email + - password + - master_key + title: UserCreate + UserLogin: + properties: + email: + type: string + format: email + title: Email + password: + type: string + title: Password + master_key: + type: string + title: Master Key + type: object + required: + - email + - password + - master_key + title: UserLogin + UserResponse: + properties: + id: + type: integer + title: Id + username: + type: string + title: Username + email: + type: string + title: Email + first_name: + anyOf: + - type: string + - type: 'null' + title: First Name + last_name: + anyOf: + - type: string + - type: 'null' + title: Last Name + is_active: + type: boolean + title: Is Active + is_verified: + type: boolean + title: Is Verified + created_at: + type: string + format: date-time + title: Created At + type: object + required: + - id + - username + - email + - first_name + - last_name + - is_active + - is_verified + - created_at + title: UserResponse + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + input: + title: Input + ctx: + type: object + title: Context + type: object + required: + - loc + - msg + - type + title: ValidationError + ValidationRequest: + properties: + site_name: + type: string + title: Site Name + description: Name of the site to validate against + credentials: + additionalProperties: + type: string + type: object + title: Credentials + description: Credentials to validate + type: object + required: + - site_name + - credentials + title: ValidationRequest + description: Request model for credential validation + example: + credentials: + email: john.doe@example.com + password: SecurePass123! + username: johndoe + site_name: github + ValidationResult: + properties: + valid: + type: boolean + title: Valid + description: Whether credentials are valid + missing_fields: + items: + type: string + type: array + title: Missing Fields + description: List of required fields that are missing + invalid_fields: + items: + type: string + type: array + title: Invalid Fields + description: List of fields with invalid values + warnings: + items: + type: string + type: array + title: Warnings + description: List of warnings about the credentials + type: object + required: + - valid + - missing_fields + - invalid_fields + - warnings + title: ValidationResult + description: Response model for credential validation + example: + invalid_fields: [] + missing_fields: [] + valid: true + warnings: [] + VerificationResponse: + properties: + request_id: + type: string + title: Request Id + status: + type: string + title: Status + code: + anyOf: + - type: string + - type: 'null' + title: Code + timestamp: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Timestamp + error_message: + anyOf: + - type: string + - type: 'null' + title: Error Message + type: object + required: + - request_id + - status + title: VerificationResponse + app__routers__chat__SignupRequest: + properties: + website_url: + type: string + title: Website Url + identity_id: + type: integer + title: Identity Id + additional_instructions: + anyOf: + - type: string + - type: 'null' + title: Additional Instructions + type: object + required: + - website_url + - identity_id + title: SignupRequest + app__routers__site_integrations__SignupRequest: + properties: + site: + type: string + title: Site + email: + type: string + format: email + title: Email + password: + type: string + title: Password + first_name: + type: string + title: First Name + last_name: + type: string + title: Last Name + company_name: + anyOf: + - type: string + - type: 'null' + title: Company Name + phone: + anyOf: + - type: string + - type: 'null' + title: Phone + job_title: + anyOf: + - type: string + - type: 'null' + title: Job Title + headless: + type: boolean + title: Headless + default: true + type: object + required: + - site + - email + - password + - first_name + - last_name + title: SignupRequest + securitySchemes: + HTTPBearer: + type: http + scheme: bearer +tags: +- name: Sites Management + description: Operations for managing automation target sites +- name: Authentication + description: User authentication and authorization +- name: Identities + description: Digital identity management +- name: Accounts + description: Account creation and management +- name: Automation + description: Automation script execution +- name: Chat + description: AI chat interface +- name: SMS + description: SMS and mobile integration +- name: Infrastructure + description: Infrastructure management +- name: LLM Scraper + description: AI-powered scraper generation +- name: Credentials + description: Secure credential management +- name: Google Integration + description: Google services integration +- name: Site Integrations + description: Website integration management +- name: Demo + description: Demo endpoints for testing diff --git a/deploy/helm/fuzekeys/files/tools.overrides.yaml b/deploy/helm/fuzekeys/files/tools.overrides.yaml new file mode 100644 index 0000000..245773d --- /dev/null +++ b/deploy/helm/fuzekeys/files/tools.overrides.yaml @@ -0,0 +1,113 @@ +# FuzeKeys — MCP mutation/reversibility overrides. +# +# Consumed by @fuzefront/mcp-gateway (MCP_TOOL_OVERRIDES). The gateway derives a +# default classification from the HTTP method; this file NARROWS it where the +# method lies about the consequence. The gateway REFUSES TO START if an entry +# here violates one of its invariants, so a bad edit fails at boot rather than at +# the first dangerous call. +# +# Keys are the `operationId`s in contracts/openapi.yaml (regenerate that file +# with `python scripts/export-openapi.py`; the ids are derived from METHOD+path +# and are stable as long as the route is). +# +# THE RULE APPLIED HERE +# --------------------- +# "Irreversible" means: the caller has no operation available to undo it. That is +# not the same as "DELETE". Two FuzeKeys-specific consequences drive most of this +# file, and neither is visible from the HTTP method: +# +# 1. DISCLOSING A SECRET IS NOT A READ. This repo is a credential vault. An +# operation that returns plaintext has permanently widened the blast radius +# of that secret the moment it responds; there is no un-disclosing it. The +# gateway cannot declare a GET `irreversible` (its invariant 3 — if a GET +# really changed state the SPEC would be what needs fixing), so the strongest +# correct signal available is `mutates: true`: it removes the operation from +# the read-only set, so a client that auto-approves reads can never reach it. +# +# 2. THIS API REACHES OUT OF THE PRODUCT. Several operations create a real +# account or a real API key on a THIRD-PARTY site. FuzeKeys cannot delete a +# Google account it created. The write happened somewhere FuzeKeys does not +# own, so no compensating FuzeKeys operation exists — irreversible. + +tools: + + # --- 1. Secret disclosure over a safe method ------------------------------ + # GET, but it delegates straight to request_account_credentials and returns + # DECRYPTED credentials. Classified as a write so it is never reachable from a + # context that believes it is only looking. + get_credentials_account_by_account_id_credentials: + mutates: true + reason: >- + Returns decrypted account credentials. A secret that has been handed out + cannot be un-handed-out, so this is never a read even though it is a GET + + # POST already defaults to a write; the reason is recorded so the classification + # survives a future reader who assumes "POST that only reads" and downgrades it. + post_credentials_request_account_credentials: + mutates: true + reason: >- + Returns decrypted account credentials — disclosure is permanent, and this + is NOT a query-shaped read despite taking a request body + post_credentials_request_identity_credentials: + mutates: true + reason: >- + Generates and returns credentials for an identity — disclosure is permanent + + # --- 2. Effects outside FuzeKeys ------------------------------------------ + post_integrations_signup: + reversibility: irreversible + reason: >- + Creates a REAL account on a third-party site. FuzeKeys has no operation + that deletes it; the account exists on someone else's system + post_integrations_apikey: + reversibility: irreversible + reason: >- + Creates a REAL API key at a third-party site. The secret is typically shown + once and FuzeKeys cannot revoke it + post_google_signup_by_identity_id: + reversibility: irreversible + reason: >- + Runs the automated Google signup flow — creates a real Google account that + FuzeKeys cannot delete + post_google_signup_manual: + reversibility: irreversible + reason: >- + Runs the manual-input Google signup flow — creates a real Google account + that FuzeKeys cannot delete + post_chat_signup: + reversibility: irreversible + reason: >- + Starts the automated signup workflow against a live site; once it runs, the + remote account exists and FuzeKeys cannot remove it + post_infrastructure_mobile_send_command: + reversibility: irreversible + reason: >- + Dispatches a command to a physical paired device. A delivered command has + already executed; there is no recall + + # --- 3. Capability-token lifecycle (backend/app/broker) ------------------- + post_broker_redeem: + reversibility: irreversible + reason: >- + Redeems a capability handle: grants default to single-use, so this both + BURNS the grant (redemption_count check in broker/service.py) and discloses + the referenced secret. Neither half can be undone + post_broker_revoke: + reversibility: irreversible + reason: >- + Revocation is permanent for that grant id — a revoked grant is never + re-armed, only a NEW grant can be issued + # `grant` and `mint-token` stay the default (write / reversible): both can be + # undone by post_broker_revoke, which is exactly what "reversible" means here. + + # --- 4. DELETEs: the default is already correct, recorded explicitly ------ + delete_identities_by_identity_id: + reversibility: irreversible + reason: >- + Deletes the identity and cascades to its accounts and stored vault + material; there is no undelete + delete_llm_scraper_scrapers_by_site_name_by_action_type: + reversibility: irreversible + reason: >- + Removes the generated scraper. The version history endpoint does not expose + a restore operation, so this is not recoverable through the API diff --git a/deploy/helm/fuzekeys/templates/mcp-gateway.yaml b/deploy/helm/fuzekeys/templates/mcp-gateway.yaml new file mode 100644 index 0000000..a19ace1 --- /dev/null +++ b/deploy/helm/fuzekeys/templates/mcp-gateway.yaml @@ -0,0 +1,128 @@ +{{- if .Values.mcp.enabled }} +{{/* +FuzeKeys MCP SSE gateway — the product's THIRD pod (backend, frontend, MCP, A2A). + +It runs the shared @fuzefront/mcp-gateway image, which contains no FuzeKeys logic +whatsoever: everything that makes this pod "the FuzeKeys gateway" is the OpenAPI +document and the upstream URL below. Every other product deploys the SAME image +with its own spec. + +The spec and the mutation overrides are mounted from a ConfigMap rendered out of +deploy/helm/fuzekeys/files/, which are COPIES of contracts/openapi.yaml and +mcp/tools.overrides.yaml. Helm cannot read files outside the chart directory, so +the copies are unavoidable — `scripts/sync-mcp-spec.sh --check` exits non-zero if +they have drifted. +*/}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "fuzekeys.fullname" . }}-mcp-spec + labels: + {{- include "fuzekeys.labels" . | nindent 4 }} + app.kubernetes.io/component: mcp-gateway +data: + openapi.yaml: |- +{{ .Files.Get "files/openapi.yaml" | indent 4 }} + tools.overrides.yaml: |- +{{ .Files.Get "files/tools.overrides.yaml" | indent 4 }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "fuzekeys.fullname" . }}-mcp + labels: + {{- include "fuzekeys.labels" . | nindent 4 }} + app.kubernetes.io/component: mcp-gateway +spec: + replicas: {{ .Values.mcp.replicas }} + selector: + matchLabels: + {{- include "fuzekeys.selectorLabels" (dict "ctx" . "component" "mcp-gateway") | nindent 6 }} + template: + metadata: + labels: + {{- include "fuzekeys.labels" . | nindent 8 }} + app.kubernetes.io/component: mcp-gateway + annotations: + # Roll the pod when the spec or the overrides change. Without this a + # contract change would leave a stale tool surface serving indefinitely — + # including a stale mutation classification, which is the dangerous half. + checksum/spec: {{ .Files.Get "files/openapi.yaml" | sha256sum }} + checksum/overrides: {{ .Files.Get "files/tools.overrides.yaml" | sha256sum }} + spec: + {{- include "fuzekeys.nodeSelector" . | nindent 6 }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: mcp-gateway + image: "{{ .Values.mcp.image.repository }}:{{ .Values.mcp.image.tag }}" + imagePullPolicy: {{ .Values.mcp.image.pullPolicy | default .Values.global.imagePullPolicy }} + ports: + - name: http + containerPort: {{ .Values.mcp.port }} + env: + - name: MCP_PRODUCT + value: {{ .Values.mcp.product | quote }} + # In-cluster base URL of the FuzeKeys REST API. Every upstream call + # goes here carrying the CALLER'S bearer token. + - name: MCP_UPSTREAM_BASE_URL + value: {{ .Values.mcp.upstreamBaseUrl | default (printf "http://%s-backend:%v" (include "fuzekeys.fullname" .) .Values.backend.port) | quote }} + - name: MCP_OPENAPI_SPEC + value: /spec/openapi.yaml + - name: MCP_TOOL_OVERRIDES + value: /spec/tools.overrides.yaml + - name: PORT + value: {{ .Values.mcp.port | quote }} + # DELIBERATELY no credential env var. The gateway forwards the caller's + # identity and holds none of its own; it refuses to start if a service + # token is present. On a credential vault that property is the whole + # point — a shared token would make every request look like the gateway + # instead of like the user, collapsing per-user authorization. + readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + failureThreshold: 6 + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 15 + periodSeconds: 15 + resources: + {{- toYaml .Values.mcp.resources | nindent 12 }} + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: ["ALL"] + volumeMounts: + - name: spec + mountPath: /spec + readOnly: true + volumes: + - name: spec + configMap: + name: {{ include "fuzekeys.fullname" . }}-mcp-spec +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "fuzekeys.fullname" . }}-mcp + labels: + {{- include "fuzekeys.labels" . | nindent 4 }} + app.kubernetes.io/component: mcp-gateway +spec: + type: ClusterIP + selector: + {{- include "fuzekeys.selectorLabels" (dict "ctx" . "component" "mcp-gateway") | nindent 4 }} + ports: + - name: http + port: {{ .Values.mcp.port }} + targetPort: http +{{- end }} diff --git a/deploy/helm/fuzekeys/values-contabo.yaml b/deploy/helm/fuzekeys/values-contabo.yaml index 9a5cb54..de3baaf 100644 --- a/deploy/helm/fuzekeys/values-contabo.yaml +++ b/deploy/helm/fuzekeys/values-contabo.yaml @@ -109,3 +109,26 @@ tokenizerRedis: enabled: false persistence: storageClass: local-path + +# ---- MCP SSE gateway: ON --------------------------------------------------- +# FuzeKeys' third pod. Matches the sibling that already deploys correctly +# (FuzeService helm/fuzeservice/values-contabo.yaml), which enables the same +# shared image in the same way. +# +# ASSUMPTION (flagged): ghcr.io/izzywdev/fuze-mcp-gateway:0.1.0 is expected to be +# published from FuzeFront packages/mcp-gateway. That package was still in flight +# at the time this was written and the tag could not be confirmed from here (an +# anonymous GHCR manifest probe returns 403 for a private package, which is +# indistinguishable from "does not exist"). If the tag is not there yet, this pod +# sits in ImagePullBackOff and NOTHING ELSE in the release is affected — flip +# `enabled: false` until it is published. +mcp: + enabled: true + replicas: 1 + image: + repository: ghcr.io/izzywdev/fuze-mcp-gateway + tag: "0.1.0" + pullPolicy: Always + # Empty = derive from the in-cluster backend Service, i.e. + # http://fuzekeys-backend:8002. Set explicitly only to override. + upstreamBaseUrl: "" diff --git a/deploy/helm/fuzekeys/values.yaml b/deploy/helm/fuzekeys/values.yaml index cd578ef..99631c4 100644 --- a/deploy/helm/fuzekeys/values.yaml +++ b/deploy/helm/fuzekeys/values.yaml @@ -296,3 +296,55 @@ tokenizerRedis: resources: requests: { cpu: 50m, memory: 64Mi } limits: { cpu: 250m, memory: 256Mi } + +# ----------------------------------------------------------------------------- +# MCP SSE gateway — FuzeKeys' THIRD pod. +# +# Runs the shared @fuzefront/mcp-gateway image (one image, one pod per product), +# configured with THIS product's OpenAPI document and API base URL. It holds no +# credential of its own and forwards the caller's Authorization header upstream, +# so every per-user check the backend makes still applies. +# +# Disabled by default: the gateway image must exist in the registry and the +# backend must be reachable at mcp.upstreamBaseUrl before this is worth +# scheduling. Enable it in the environment overlay (values-contabo.yaml). +# ----------------------------------------------------------------------------- +mcp: + enabled: false + replicas: 1 + port: 8081 + # MCP server name reported to clients. Uses the DE-PREFIXED product slug, to + # match registration/manifest.json. + product: keys + image: + repository: ghcr.io/izzywdev/fuze-mcp-gateway + tag: "0.1.0" + pullPolicy: IfNotPresent + # Base URL of the FuzeKeys REST API. Empty = derive it from the in-cluster + # backend Service (-backend:), which is correct for + # every normal deployment. The paths in contracts/openapi.yaml are absolute + # (/api/v1/...), so this URL carries NO path prefix. + upstreamBaseUrl: "" + resources: + requests: { cpu: 50m, memory: 64Mi } + limits: { cpu: 500m, memory: 256Mi } + +# ----------------------------------------------------------------------------- +# A2A — NOT a pod in this chart, by design. +# +# There is no a2a Deployment here and there must not be one. The published image +# (ghcr.io/izzywdev/fuzeagent-a2a) is built as ONE multi-tenant server, and its +# card generator proves it: agent-templates/a2a/card_generator.py line 29 defines +# +# IN_CLUSTER_URL = "http://a2a-shared.fuzeagent.svc.cluster.local:8080/rpc" +# +# and `_interface()` returns that CONSTANT as the callable endpoint of every +# non-external Agent Card. It is not values-driven and there is no env var that +# overrides it. A per-product pod would therefore boot, pass its health check, and +# advertise the SHARED server's address — a pod that looks healthy and is +# functionally dead, which is worse than no pod at all. +# +# Onboarding this product to A2A is a `tenants[]` entry in FuzeAgent's +# deploy/helm/a2a-shared values. The exact entry, and the preconditions this repo +# does not yet meet, are written down in docs/a2a-tenant-registration.md. +# ----------------------------------------------------------------------------- diff --git a/docs/a2a-tenant-registration.md b/docs/a2a-tenant-registration.md new file mode 100644 index 0000000..40b9fe5 --- /dev/null +++ b/docs/a2a-tenant-registration.md @@ -0,0 +1,84 @@ +# A2A onboarding — a tenant entry in FuzeAgent, not a pod here + +## Why there is no A2A pod in this repo's chart + +The published image `ghcr.io/izzywdev/fuzeagent-a2a` is built as **one +multi-tenant server**, and the card generator makes that structural rather than +conventional. `agent-templates/a2a/card_generator.py` line 29: + +```python +IN_CLUSTER_URL = "http://a2a-shared.fuzeagent.svc.cluster.local:8080/rpc" +``` + +`_interface()` returns that **constant** as the callable endpoint of every +non-external Agent Card. It is not values-driven, and no environment variable +overrides it — the only branch is `external: true`, which swaps in a public +`https://a2a..prod.fuzefront.com/rpc` tunnel URL. + +So a per-product A2A Deployment would start, pass its health check, and publish a +card advertising the **shared** server's address. Every caller would dial +`a2a-shared.fuzeagent`, and the per-product pod would never receive a request. A +pod that looks healthy and is functionally dead is worse than no pod, because +nothing goes red. + +FuzeAgent's own `docs/a2a/enable-your-pod.md` says the same thing in prose: +*"There is one shared A2A server for the whole family, not one deployment per +product... onboarding a repo is data, not a new chart."* + +**Also worth correcting, because it drives the wrong design:** A2A is not an +"LLM → REST/OpenAPI" bridge. It reads no OpenAPI document and proxies no REST +call. It is a JSON-RPC 2.0 agent-delegation server whose Agent Card is +*projected* from `.fuze/manifest.json` plus `agent-templates/roles//role.json`. +The OpenAPI-to-tools job belongs to the MCP gateway, which **is** genuinely +per-product and **is** deployed by this chart. + +## The tenant entry this product needs + +Add to FuzeAgent `deploy/helm/a2a-shared/values-prod.yaml` under `a2a.tenants`: + +```yaml + - tenant: FuzeKeys + repo: izzywdev/FuzeKeys + ref: master + enabled: true + external: false + entryRole: keys-broker + servingRoles: [keys-broker] + provider: + name: anthropic +``` + +Both gates must be true to actually serve: `a2a.enabled` (the shared server is +deployed at all) and this tenant's own `enabled`. + +## Preconditions this repo does NOT yet meet + +These are the callee's own contract and every one of them **fails closed**. None +was worked around here. + +1. **`agent-templates/roles/keys-broker/role.json` has no `description`.** It has + `summary`. The projection requires `description` on every serving role and + **fails** rather than shipping a placeholder (card-projection.md §3). This is + the one blocker that is genuinely this repo's to fix, and it belongs to + `a2a-maintainer` — authoring agent-card content is not a chart change, so it + is reported here rather than guessed at. + +2. **`.fuze/manifest.json` already has a populated `providesTo`** (18 entries) — + good. An absent or empty list means DENY EVERY CALLER (authz.md §3), so this + product is ahead of its siblings here. + +3. **`.fuze/manifest.json` has `a2a.enabled: false`** with a note saying it stays + false until `a2a-maintainer` validates the card projection against the frozen + contract. That note is still accurate; nothing in this PR changes it. + +## Where the values come from + +`tenant`, `repo`, `ref` and `enabled` are per-tenant. `auth.oidcIssuerUrl`, +`auth.audience`, `auth.callerClaim` and `cardSigning.keySecretRef` are +**server-level** and already belong to the shared deployment — this product does +not set them and must not carry a copy. Secret values never appear in values; +every `*SecretRef` is a `{name, key}` pointer to a SealedSecret-provisioned +Secret. + +Reference: `agent-templates/contracts/a2a/v1/schema/values-interface.schema.json` +(frozen) and `docs/a2a/enable-your-pod.md`, both in FuzeAgent. diff --git a/mcp/tools.overrides.yaml b/mcp/tools.overrides.yaml new file mode 100644 index 0000000..245773d --- /dev/null +++ b/mcp/tools.overrides.yaml @@ -0,0 +1,113 @@ +# FuzeKeys — MCP mutation/reversibility overrides. +# +# Consumed by @fuzefront/mcp-gateway (MCP_TOOL_OVERRIDES). The gateway derives a +# default classification from the HTTP method; this file NARROWS it where the +# method lies about the consequence. The gateway REFUSES TO START if an entry +# here violates one of its invariants, so a bad edit fails at boot rather than at +# the first dangerous call. +# +# Keys are the `operationId`s in contracts/openapi.yaml (regenerate that file +# with `python scripts/export-openapi.py`; the ids are derived from METHOD+path +# and are stable as long as the route is). +# +# THE RULE APPLIED HERE +# --------------------- +# "Irreversible" means: the caller has no operation available to undo it. That is +# not the same as "DELETE". Two FuzeKeys-specific consequences drive most of this +# file, and neither is visible from the HTTP method: +# +# 1. DISCLOSING A SECRET IS NOT A READ. This repo is a credential vault. An +# operation that returns plaintext has permanently widened the blast radius +# of that secret the moment it responds; there is no un-disclosing it. The +# gateway cannot declare a GET `irreversible` (its invariant 3 — if a GET +# really changed state the SPEC would be what needs fixing), so the strongest +# correct signal available is `mutates: true`: it removes the operation from +# the read-only set, so a client that auto-approves reads can never reach it. +# +# 2. THIS API REACHES OUT OF THE PRODUCT. Several operations create a real +# account or a real API key on a THIRD-PARTY site. FuzeKeys cannot delete a +# Google account it created. The write happened somewhere FuzeKeys does not +# own, so no compensating FuzeKeys operation exists — irreversible. + +tools: + + # --- 1. Secret disclosure over a safe method ------------------------------ + # GET, but it delegates straight to request_account_credentials and returns + # DECRYPTED credentials. Classified as a write so it is never reachable from a + # context that believes it is only looking. + get_credentials_account_by_account_id_credentials: + mutates: true + reason: >- + Returns decrypted account credentials. A secret that has been handed out + cannot be un-handed-out, so this is never a read even though it is a GET + + # POST already defaults to a write; the reason is recorded so the classification + # survives a future reader who assumes "POST that only reads" and downgrades it. + post_credentials_request_account_credentials: + mutates: true + reason: >- + Returns decrypted account credentials — disclosure is permanent, and this + is NOT a query-shaped read despite taking a request body + post_credentials_request_identity_credentials: + mutates: true + reason: >- + Generates and returns credentials for an identity — disclosure is permanent + + # --- 2. Effects outside FuzeKeys ------------------------------------------ + post_integrations_signup: + reversibility: irreversible + reason: >- + Creates a REAL account on a third-party site. FuzeKeys has no operation + that deletes it; the account exists on someone else's system + post_integrations_apikey: + reversibility: irreversible + reason: >- + Creates a REAL API key at a third-party site. The secret is typically shown + once and FuzeKeys cannot revoke it + post_google_signup_by_identity_id: + reversibility: irreversible + reason: >- + Runs the automated Google signup flow — creates a real Google account that + FuzeKeys cannot delete + post_google_signup_manual: + reversibility: irreversible + reason: >- + Runs the manual-input Google signup flow — creates a real Google account + that FuzeKeys cannot delete + post_chat_signup: + reversibility: irreversible + reason: >- + Starts the automated signup workflow against a live site; once it runs, the + remote account exists and FuzeKeys cannot remove it + post_infrastructure_mobile_send_command: + reversibility: irreversible + reason: >- + Dispatches a command to a physical paired device. A delivered command has + already executed; there is no recall + + # --- 3. Capability-token lifecycle (backend/app/broker) ------------------- + post_broker_redeem: + reversibility: irreversible + reason: >- + Redeems a capability handle: grants default to single-use, so this both + BURNS the grant (redemption_count check in broker/service.py) and discloses + the referenced secret. Neither half can be undone + post_broker_revoke: + reversibility: irreversible + reason: >- + Revocation is permanent for that grant id — a revoked grant is never + re-armed, only a NEW grant can be issued + # `grant` and `mint-token` stay the default (write / reversible): both can be + # undone by post_broker_revoke, which is exactly what "reversible" means here. + + # --- 4. DELETEs: the default is already correct, recorded explicitly ------ + delete_identities_by_identity_id: + reversibility: irreversible + reason: >- + Deletes the identity and cascades to its accounts and stored vault + material; there is no undelete + delete_llm_scraper_scrapers_by_site_name_by_action_type: + reversibility: irreversible + reason: >- + Removes the generated scraper. The version history endpoint does not expose + a restore operation, so this is not recoverable through the API diff --git a/registration/manifest.json b/registration/manifest.json index 41f5638..620f59a 100644 --- a/registration/manifest.json +++ b/registration/manifest.json @@ -1,7 +1,7 @@ { "manifestVersion": "1", - "slug": "fuzekeys", - "name": "FuzeKeys", + "slug": "keys", + "name": "Keys", "menuLabel": "Keys", "description": "Identity and credential vault — managed digital identities, per-site accounts, encrypted credentials, and automated signup workflows.", "icon": { "kind": "emoji", "value": "🔑" }, diff --git a/scripts/export-openapi.py b/scripts/export-openapi.py new file mode 100755 index 0000000..2fd1c86 --- /dev/null +++ b/scripts/export-openapi.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +"""Export FuzeKeys' OpenAPI document to ``contracts/openapi.yaml``. + +WHY THIS IS DERIVED AND NOT HAND-WRITTEN +---------------------------------------- +The FastAPI application IS the API. A hand-authored contract for 65 paths would +be wrong within a week and nobody would notice, because nothing compares the two. +Deriving the document from the running application makes "the contract describes +the real endpoints" a property of the build rather than a promise in a review. + +``--check`` re-derives the document and compares the set of ``(method, path)`` +operations against the committed ``contracts/openapi.yaml``. It exits non-zero +when they differ, which is the only failure mode that matters downstream: the +MCP gateway turns every operation in the committed document into a tool, so an +operation that exists in the code but not in the contract is an invisible tool, +and one in the contract but not the code is a tool that 404s. + +The comparison deliberately ignores schemas and ``operationId`` (see below) — +this is a coverage gate, not a byte-diff. + +OPERATION IDs +------------- +FastAPI's auto-generated ``operationId`` (``get_account_credentials_api_ +credentials_account__account_id__credentials_get``) is up to 76 characters, and +the MCP gateway truncates tool names at 64 — two different operations can +therefore collide into one tool name. The live application also currently emits +DUPLICATE operationIds for two accounts routes (FastAPI warns about it at +startup). So this script assigns its own deterministic, readable id derived from +``METHOD + path``, which is unique by construction and short: + + GET /api/credentials/account/{account_id}/credentials + -> get_credentials_account_by_account_id_credentials + +The application's own ``/openapi.json`` keeps FastAPI's ids. That divergence is +intentional and narrow: ``operationId`` is metadata, not API shape, and fixing it +in the application would mean editing routers another workstream is currently +rewriting. + +Usage: + python scripts/export-openapi.py # write contracts/openapi.yaml + python scripts/export-openapi.py --check # verify no drift, write nothing + +Run it from the repo root with the backend's dependencies importable. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +BACKEND = REPO_ROOT / "backend" +CONTRACT = REPO_ROOT / "contracts" / "openapi.yaml" + +HTTP_METHODS = ("get", "put", "post", "delete", "patch", "head", "options", "trace") + + +def operation_id(method: str, path: str) -> str: + """Deterministic, unique-by-construction, MCP-legal operation id. + + Mirrors the gateway's own fallback naming (``_``) so a + reader of the tool list can map a tool straight back to an HTTP route. + """ + slug = path + for prefix in ("/api/v1/", "/api/"): + if slug.startswith(prefix): + slug = slug[len(prefix):] + break + out = [] + i = 0 + while i < len(slug): + ch = slug[i] + if ch == "{": + end = slug.index("}", i) + out.append("by_" + slug[i + 1:end]) + i = end + 1 + else: + out.append(ch) + i += 1 + slug = "".join(out) + cleaned = "".join(c if c.isalnum() else "_" for c in slug).strip("_") + while "__" in cleaned: + cleaned = cleaned.replace("__", "_") + name = f"{method.lower()}_{cleaned}" if cleaned else f"{method.lower()}_root" + return name[:64] + + +def build_spec() -> dict: + sys.path.insert(0, str(BACKEND)) + from app.main import app # noqa: E402 (import after sys.path fix) + + spec = app.openapi() + + # Trim the marketing prose out of `info.description`: it is rendered into + # every MCP client's server description and says nothing an agent can use. + info = spec.setdefault("info", {}) + info["description"] = ( + "FuzeKeys — identity and credential vault. Managed digital identities, " + "per-site accounts, encrypted vault assets, capability-token secret " + "brokering, and automated signup workflows.\n\n" + "DERIVED DOCUMENT: regenerate with `python scripts/export-openapi.py`. " + "Do not hand-edit." + ) + info.pop("termsOfService", None) + info.pop("contact", None) + + seen: dict[str, str] = {} + for path, item in spec.get("paths", {}).items(): + for method in HTTP_METHODS: + op = item.get(method) + if not isinstance(op, dict): + continue + oid = operation_id(method, path) + if oid in seen: + raise SystemExit( + f"operationId collision: {oid} claimed by {seen[oid]} and " + f"{method.upper()} {path}" + ) + seen[oid] = f"{method.upper()} {path}" + op["operationId"] = oid + return spec + + +def operations(spec: dict) -> set[str]: + return { + f"{method.upper()} {path}" + for path, item in (spec.get("paths") or {}).items() + for method in HTTP_METHODS + if isinstance(item.get(method), dict) + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--check", + action="store_true", + help="compare against the committed contract; write nothing", + ) + args = parser.parse_args() + + import yaml + + spec = build_spec() + + if args.check: + if not CONTRACT.exists(): + print(f"MISSING: {CONTRACT} does not exist", file=sys.stderr) + return 1 + committed = yaml.safe_load(CONTRACT.read_text(encoding="utf-8")) + live, on_disk = operations(spec), operations(committed) + missing = sorted(live - on_disk) + extra = sorted(on_disk - live) + if missing or extra: + for m in missing: + print(f"MISSING FROM CONTRACT: {m}", file=sys.stderr) + for e in extra: + print(f"NOT IN THE APPLICATION: {e}", file=sys.stderr) + print( + "\ncontracts/openapi.yaml is out of date — run " + "`python scripts/export-openapi.py`.", + file=sys.stderr, + ) + return 1 + print(f"contracts/openapi.yaml is current ({len(live)} operations).") + return 0 + + CONTRACT.parent.mkdir(parents=True, exist_ok=True) + CONTRACT.write_text( + "# GENERATED — do not hand-edit.\n" + "# Source of truth: the FastAPI application (backend/app/main.py).\n" + "# Regenerate: python scripts/export-openapi.py\n" + "# Verify: python scripts/export-openapi.py --check\n" + + yaml.safe_dump(spec, sort_keys=False, width=100, allow_unicode=True), + encoding="utf-8", + ) + print(f"wrote {CONTRACT} ({len(operations(spec))} operations)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/sync-mcp-spec.sh b/scripts/sync-mcp-spec.sh new file mode 100755 index 0000000..993b829 --- /dev/null +++ b/scripts/sync-mcp-spec.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Keep the chart's copies of the MCP inputs identical to the sources of truth. +# +# Helm can only read files inside the chart directory, so the OpenAPI document +# and the mutation overrides that the MCP gateway pod mounts have to exist twice: +# +# contracts/openapi.yaml -> deploy/helm/fuzekeys/files/openapi.yaml +# mcp/tools.overrides.yaml -> deploy/helm/fuzekeys/files/tools.overrides.yaml +# +# A silently stale copy is the failure that matters: the gateway would keep +# serving the OLD tool surface — including the OLD mutation classification — with +# nothing anywhere reporting a problem. `--check` turns that into a red build. +# +# ./scripts/sync-mcp-spec.sh # copy source -> chart +# ./scripts/sync-mcp-spec.sh --check # exit 1 if they differ; copy nothing +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +DEST="$ROOT/deploy/helm/fuzekeys/files" + +PAIRS=( + "contracts/openapi.yaml:openapi.yaml" + "mcp/tools.overrides.yaml:tools.overrides.yaml" +) + +if [[ "${1:-}" == "--check" ]]; then + rc=0 + for pair in "${PAIRS[@]}"; do + src="$ROOT/${pair%%:*}" + dst="$DEST/${pair##*:}" + if [[ ! -f "$dst" ]]; then + echo "MISSING: $dst" >&2 + rc=1 + elif ! diff -q "$src" "$dst" >/dev/null; then + echo "DRIFT: $dst differs from ${pair%%:*}" >&2 + diff -u "$src" "$dst" | head -40 >&2 || true + rc=1 + fi + done + if [[ $rc -ne 0 ]]; then + echo "" >&2 + echo "Run ./scripts/sync-mcp-spec.sh to refresh the chart's copies." >&2 + exit 1 + fi + echo "chart MCP inputs are in sync." + exit 0 +fi + +mkdir -p "$DEST" +for pair in "${PAIRS[@]}"; do + cp "$ROOT/${pair%%:*}" "$DEST/${pair##*:}" + echo "synced ${pair%%:*} -> deploy/helm/fuzekeys/files/${pair##*:}" +done