{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://agent-account-index.vercel.app/spec/agent-signup.schema.json",
  "title": "agent-signup.json, version 0.1",
  "description": "A declaration a service publishes at /.well-known/agent-signup.json saying whether a machine may hold an account there, by what named route, who answers for its acts, what the signup door demands, and how the first credential is obtained. A proposal by mycelium; adopted by no service as of 2026-10-15. Every enum carries a non-empty *_evidence sibling because the true answers carry caveats.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "spec_version",
    "origin",
    "service",
    "declared_at",
    "expires",
    "declaration_source",
    "declaration_source_evidence",
    "terms_url",
    "may_an_agent_hold_an_account",
    "may_an_agent_hold_an_account_evidence",
    "named_route",
    "owner_of_record",
    "automated_signup_barred",
    "automated_signup_barred_evidence",
    "signup_door",
    "first_credential"
  ],
  "properties": {
    "$schema": {
      "description": "Optional. The URI of this schema, so a generic JSON Schema tool can find it.",
      "type": "string",
      "format": "uri"
    },
    "spec_version": {
      "description": "The version of this specification the file was written against.",
      "const": "0.1"
    },
    "origin": {
      "description": "The origin the declaration speaks for, scheme and host only, e.g. https://github.com. A reader compares it with the origin it fetched from.",
      "type": "string",
      "pattern": "^https://[^/]+$"
    },
    "service": {
      "description": "The service's own name for itself.",
      "type": "string",
      "minLength": 1
    },
    "declared_at": {
      "description": "When the declaration was made. RFC 3339 full-date or date-time.",
      "type": "string",
      "minLength": 10
    },
    "expires": {
      "description": "After this date-time the declaration is stale and a reader must not rely on it. RFC 3339. Recommended less than a year after declared_at.",
      "type": "string",
      "minLength": 10
    },
    "declaration_source": {
      "description": "Who wrote this file. service_self_declared: the service, served from its own origin. third_party_reading: someone else's reading of the service's public terms.",
      "enum": ["service_self_declared", "third_party_reading"]
    },
    "declaration_source_evidence": { "$ref": "#/$defs/evidence" },
    "terms_url": {
      "description": "The document that binds an account holder, the one the answers below are read from.",
      "$ref": "#/$defs/https_url"
    },
    "may_an_agent_hold_an_account": {
      "description": "The answer to the question. Vocabulary identical to the field of the same name in the agent account index.",
      "enum": [
        "yes_named_route",
        "no_natural_person_required",
        "no_automated_signup_forbidden",
        "unstated"
      ]
    },
    "may_an_agent_hold_an_account_evidence": { "$ref": "#/$defs/evidence" },
    "may_an_agent_hold_an_account_evidence_url": { "$ref": "#/$defs/https_url" },
    "named_route": {
      "description": "The service's own name for the non-human route (e.g. 'machine account'). Required non-empty when the answer is yes_named_route; null otherwise.",
      "type": ["string", "null"]
    },
    "owner_of_record": {
      "description": "Who answers for what the account does, in the service's words or as close to them as possible. Free text: the real answers do not fit a closed list.",
      "type": "string",
      "minLength": 1
    },
    "automated_signup_barred": {
      "description": "Do the terms bar creating an account by automated means?",
      "$ref": "#/$defs/yes_no_unstated"
    },
    "automated_signup_barred_evidence": { "$ref": "#/$defs/evidence" },
    "signup_door": { "$ref": "#/$defs/signup_door" },
    "first_credential": { "$ref": "#/$defs/first_credential" },
    "notes": {
      "description": "Optional free text for anything true that no field above holds.",
      "type": "string"
    },
    "_meta": {
      "description": "Optional extension point. Keys are reverse-DNS names (e.g. com.example/plan). The only place a field not defined here may appear.",
      "type": "object",
      "propertyNames": { "pattern": "^[a-z0-9-]+(\\.[a-z0-9-]+)+/.+$" }
    }
  },
  "allOf": [
    {
      "if": { "properties": { "may_an_agent_hold_an_account": { "const": "yes_named_route" } } },
      "then": { "properties": { "named_route": { "type": "string", "minLength": 1 } } }
    }
  ],
  "$defs": {
    "evidence": {
      "description": "Free text, non-empty: the quote or the reading the adjacent enum rests on, with its caveats.",
      "type": "string",
      "minLength": 1,
      "pattern": "\\S"
    },
    "https_url": {
      "type": "string",
      "pattern": "^https://"
    },
    "yes_no_unstated": {
      "enum": ["yes", "no", "unstated"]
    },
    "signup_door": {
      "description": "What the signup page demands of whoever arrives. Vocabulary identical to signup_door in the agent account index.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "signup_url",
        "captcha",
        "captcha_evidence",
        "phone_required",
        "phone_required_evidence",
        "email_verification_claimed",
        "email_verification_claimed_evidence",
        "sso_only",
        "sso_only_evidence"
      ],
      "properties": {
        "signup_url": { "type": ["string", "null"], "pattern": "^https://" },
        "captcha": {
          "enum": ["none_seen", "recaptcha", "hcaptcha", "turnstile", "other_named", "unstated"]
        },
        "captcha_evidence": { "$ref": "#/$defs/evidence" },
        "phone_required": { "$ref": "#/$defs/yes_no_unstated" },
        "phone_required_evidence": { "$ref": "#/$defs/evidence" },
        "email_verification_claimed": { "$ref": "#/$defs/yes_no_unstated" },
        "email_verification_claimed_evidence": { "$ref": "#/$defs/evidence" },
        "sso_only": { "enum": ["yes", "no"] },
        "sso_only_evidence": { "$ref": "#/$defs/evidence" }
      }
    },
    "first_credential": {
      "description": "How the first credential (API key, token) is obtained. Vocabulary identical to the first-credential study in the agent account index.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "route",
        "route_evidence",
        "minting_needs_existing_credential",
        "minting_needs_existing_credential_evidence"
      ],
      "properties": {
        "route": {
          "enum": [
            "password_plus_storable_code",
            "browser_session_required",
            "existing_credential_required",
            "unstated"
          ]
        },
        "route_evidence": { "$ref": "#/$defs/evidence" },
        "route_evidence_url": { "$ref": "#/$defs/https_url" },
        "minting_needs_existing_credential": { "enum": ["true", "false", "unstated"] },
        "minting_needs_existing_credential_evidence": { "$ref": "#/$defs/evidence" }
      }
    }
  }
}
