{
  "name": "vibecheck-api",
  "version": "1.3.0",
  "protocol": "vibecheck-api/1",
  "description": "JSON shape for vibecheck inspection reports.",
  "endpoints": {
    "scan": {
      "method": "POST",
      "path": "/api/scan",
      "contentType": "application/json",
      "request": {
        "url": "string (required) — http(s) URL to inspect",
        "consent": "boolean (required) — must be true; operator confirms permission",
        "deep": "boolean (optional) — enable subdomain CT enumeration; +20-40s",
        "list_publicly": "boolean (optional) — currently no-op; will gate KV-backed shareable URLs in v1.x"
      },
      "response": {
        "targetUrl": "string",
        "scannedAt": "number (unix-seconds float)",
        "durationMs": "number",
        "fetcherErrors": "string[]",
        "bundleCount": "number — count of external <script src> bundles fetched",
        "overallSeverity": "critical | high | medium | low | info",
        "grade": "A | B | C | D | F",
        "deepScan": "boolean",
        "supabase": "SupabaseFinding[] — see types below",
        "firebase": "FirebaseFinding[]",
        "secrets": "SecretFinding[]",
        "paths": "PathFinding[]",
        "api": "ApiFinding[]",
        "headers": "HeaderFinding[]",
        "storage": "StorageFinding[]",
        "framework": "FrameworkFinding[]",
        "prompts": "PromptFinding[]",
        "defaults": "DefaultsFinding[]",
        "leaks": "LeakFinding[]",
        "baas": "BaasFinding[]",
        "subdomains": "SubdomainFinding[] — populated only when deepScan: true",
        "redirects": "RedirectFinding[]",
        "jwts": "JwtFinding[]"
      }
    },
    "version": {
      "method": "GET",
      "path": "/api/version"
    },
    "health": {
      "method": "GET",
      "path": "/api/health"
    },
    "mcp": {
      "method": "POST",
      "path": "/api/mcp",
      "protocol": "MCP / JSON-RPC 2.0"
    }
  },
  "types": {
    "Severity": [
      "critical",
      "high",
      "medium",
      "low",
      "info"
    ],
    "Grade": [
      "A",
      "B",
      "C",
      "D",
      "F"
    ],
    "SupabaseFinding": {
      "project": {
        "projectRef": "string",
        "url": "string",
        "keys": "Record<string,string> — keyed by role name"
      },
      "rlsFindings": "TableFinding[]",
      "notes": "string[]",
      "severity": "Severity"
    },
    "TableFinding": {
      "table": "string",
      "publiclyReadable": "boolean",
      "rowCountSample": "number — 0 or 1; we only sample limit=1",
      "piiColumns": "string[] — column names matching PII patterns (email, phone, etc.)",
      "allColumns": "string[]",
      "error": "string (optional)"
    },
    "FirebaseFinding": {
      "projectId": "string",
      "rtdbOpen": "boolean | null",
      "notes": "string[]",
      "severity": "Severity"
    },
    "SecretFinding": {
      "rule": "string — see ruleNames[]",
      "severity": "Severity",
      "description": "string",
      "redactedMatch": "string — first 6 + last 4 chars only",
      "location": "string — '<html>' | '<inline:N>' | <bundle URL> | 'sourcemap:<path>'"
    },
    "PathFinding": {
      "path": "string",
      "severity": "Severity",
      "description": "string",
      "status": "number",
      "evidence": "string (optional)"
    },
    "ApiFinding": {
      "endpoint": "string",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    },
    "HeaderFinding": {
      "rule": "string — see ruleNames[]",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    },
    "StorageFinding": {
      "provider": "s3 | gcs | azure | r2",
      "bucket": "string",
      "url": "string",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    },
    "FrameworkFinding": {
      "rule": "string — see ruleNames[]",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    },
    "PromptFinding": {
      "rule": "string",
      "severity": "Severity",
      "description": "string",
      "evidence": "string",
      "location": "string"
    },
    "DefaultsFinding": {
      "platform": "string",
      "endpoint": "string",
      "severity": "Severity",
      "description": "string"
    },
    "LeakFinding": {
      "rule": "string — see ruleNames[]",
      "severity": "Severity",
      "description": "string",
      "evidence": "string",
      "location": "string"
    },
    "BaasFinding": {
      "provider": "convex | appwrite | pocketbase",
      "endpoint": "string",
      "collection": "string (optional)",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    },
    "SubdomainFinding": {
      "subdomain": "string",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    },
    "RedirectFinding": {
      "url": "string",
      "param": "string",
      "severity": "Severity",
      "description": "string",
      "evidence": "string (optional)"
    }
  },
  "ruleCatalogue": {
    "total": 175,
    "docs": "https://vibecheck.themeridianlab.com/fix",
    "names": [
      "android_assetlinks_exposed",
      "anthropic_key",
      "anthropic_key_in_client",
      "anyscale_token",
      "api_key_in_url",
      "apple_aasa_exposed",
      "appwrite_collection_public_read",
      "asana_token",
      "aspnet_version_leak",
      "atlassian_api_token",
      "auth0_management_token",
      "auth_page_missing_csp",
      "auth_page_missing_hsts",
      "auth_token_in_localstorage",
      "auth_token_in_url",
      "auth_token_in_websocket_url",
      "aws_access_key_id",
      "azure_storage_key",
      "bearer_token_inline",
      "brevo_api_key",
      "cloudflare_api_token",
      "cohere_token",
      "convex_deployment_exposed",
      "convex_function_no_auth",
      "cookie_missing_httponly",
      "cookie_missing_samesite",
      "cookie_missing_secure",
      "cookie_name_reveals_stack",
      "cors_acao_wildcard_with_credentials",
      "cors_null_origin_allowed",
      "cors_origin_reflected",
      "cors_origin_reflected_with_credentials",
      "csp_data_uri_in_script_src",
      "csp_external_script_blocked",
      "csp_inline_script_blocked",
      "csp_inline_script_hash_pinned",
      "csp_missing_default_src",
      "csp_report_only",
      "csp_unsafe_eval",
      "csp_unsafe_inline_scripts",
      "csp_wildcard_script_src",
      "datadog_api_key_labelled",
      "datadog_app_key_labelled",
      "deepgram_key",
      "discord_webhook_url",
      "drupal_changelog_exposed",
      "elevenlabs_key",
      "exposed_composer_lock",
      "exposed_env_development",
      "exposed_env_file",
      "exposed_env_staging",
      "exposed_gemfile_lock",
      "exposed_git_directory",
      "exposed_htpasswd",
      "exposed_nextjs_build_manifest",
      "exposed_rails_database_yml",
      "exposed_rails_secrets_yml",
      "exposed_sourcemap",
      "exposed_web_config",
      "fal_ai_key",
      "firebase_project_exposed",
      "firebase_rtdb_open",
      "firebase_storage_public_list",
      "firestore_collection_public_read",
      "fly_io_token",
      "generator_tag",
      "github_token",
      "github_webhook_secret",
      "google_api_key",
      "graphql_introspection_enabled",
      "graphql_playground_exposed",
      "groq_key",
      "honeycomb_api_key",
      "html_comment_leak",
      "hubspot_api_key",
      "huggingface_token",
      "huggingface_token_old",
      "iframe_dangerous_allow",
      "internal_ip_in_client",
      "js_comment_leak",
      "jwt_admin_in_client",
      "jwt_alg_none",
      "jwt_expired_in_client",
      "jwt_long_lived",
      "jwt_no_expiration",
      "klaviyo_private_key",
      "linear_api_key",
      "llm_prompt_template_in_client",
      "loops_key",
      "mailgun_key",
      "mapbox_secret_token",
      "microsoft_entra_association_exposed",
      "missing_frame_protection",
      "missing_security_txt",
      "missing_sri_external_script",
      "missing_sri_external_stylesheet",
      "missing_trusted_types",
      "mixed_content",
      "modal_token",
      "mongodb_connection_string",
      "netlify_token",
      "nextjs_dev_in_production",
      "notion_integration_token",
      "npm_token",
      "oauth_client_secret_in_client",
      "oauth_implicit_flow",
      "oauth_missing_state",
      "oauth_redirect_uri_dynamic",
      "oauth_redirect_uri_http",
      "oauth_redirect_uri_localhost",
      "open_redirect",
      "open_supabase_storage_bucket",
      "openai_key",
      "openai_key_in_client",
      "openai_org_id",
      "openapi_spec_exposed",
      "openid_configuration_exposed",
      "openid_credential_issuer_exposed",
      "password_field_autocomplete_off",
      "password_field_no_autocomplete",
      "password_form_action_http",
      "password_form_over_http",
      "perplexity_key",
      "php_error_disclosed",
      "pinecone_key",
      "plaid_secret",
      "pocketbase_collections_metadata_public",
      "pocketbase_list_rule_blank",
      "postgres_connection_string",
      "posthog_api_key",
      "postmark_token",
      "postmessage_listener_no_origin_check",
      "postmessage_wildcard_target",
      "private_key_block",
      "pypi_token",
      "react_devtools_enabled",
      "redis_connection_string",
      "redux_devtools_enabled",
      "render_api_key",
      "replicate_token",
      "resend_key",
      "rls_policy_not_working",
      "robots_blocks_ai_crawlers",
      "robots_txt_reveals_paths",
      "rollbar_token",
      "sendgrid_key",
      "sentry_auth_token",
      "sentry_dsn",
      "server_version_leak",
      "service_role_key_exposed",
      "service_worker_cross_origin",
      "service_worker_imports_cross_origin",
      "service_worker_origin_wide_scope",
      "slack_bot_token",
      "slack_signing_secret",
      "slack_webhook_url",
      "staging_hostname_in_client",
      "stripe_connect_account_id",
      "stripe_restricted_key",
      "stripe_secret_key",
      "stripe_secret_key_in_client",
      "stripe_webhook_secret",
      "supabase_anon_only_no_rls",
      "supabase_service_role_in_client",
      "supabase_service_role_jwt_pattern",
      "swagger_ui_exposed",
      "together_ai_key",
      "twilio_account_sid",
      "twilio_auth_token",
      "verbose_error_stack_trace",
      "vercel_token",
      "vue_devtools_enabled",
      "webhook_secret_generic",
      "wordpress_readme_exposed",
      "x_powered_by_leak"
    ]
  },
  "fixGuides": {
    "pattern": "https://vibecheck.themeridianlab.com/fix/<rule-name>",
    "example": "https://vibecheck.themeridianlab.com/fix/stripe_secret_key"
  },
  "mcp": {
    "endpoint": "https://vibecheck.themeridianlab.com/api/mcp",
    "docs": "https://vibecheck.themeridianlab.com/agents",
    "tools": [
      "vibecheck_scan",
      "vibecheck_secrets",
      "vibecheck_rls_sql"
    ]
  },
  "ethics": {
    "readOnly": true,
    "neverUseDiscoveredCredentials": true,
    "maxRowsSampledPerTable": 1,
    "retentionPolicy": "Reports are not stored on our servers in v1. Sharing happens via base64-encoded URL fragments processed client-side."
  },
  "docs": {
    "humanReadable": "https://vibecheck.themeridianlab.com/agents",
    "llmsTxt": "https://vibecheck.themeridianlab.com/llms.txt",
    "llmsFullTxt": "https://vibecheck.themeridianlab.com/llms-full.txt"
  }
}