{
  "feature": "identity-lookup",
  "version": "1.0.0",
  "description": "Bridge between user contact details (email, phone) and messaging identities via external identity servers. Enables invitations before account creation and contact binding.",
  "category": "auth",
  "tags": [
    "identity",
    "3pid",
    "email",
    "phone",
    "lookup",
    "binding",
    "verification"
  ],
  "actors": [
    {
      "id": "user",
      "name": "User",
      "type": "human",
      "description": "Account owner binding or looking up an identity"
    },
    {
      "id": "homeserver",
      "name": "Homeserver",
      "type": "system",
      "description": "Server proxying identity requests and enforcing rate limits"
    },
    {
      "id": "identity_server",
      "name": "Identity Server",
      "type": "external",
      "description": "External service storing bindings between contact details and account identifiers"
    }
  ],
  "fields": [
    {
      "name": "medium",
      "type": "select",
      "required": true,
      "label": "Type of third-party identifier",
      "options": [
        {
          "value": "email",
          "label": "Email"
        },
        {
          "value": "msisdn",
          "label": "Phone (MSISDN)"
        }
      ]
    },
    {
      "name": "address",
      "type": "text",
      "required": true,
      "label": "The email address or phone number being looked up or bound"
    },
    {
      "name": "user_id",
      "type": "text",
      "required": false,
      "label": "The messaging account identifier to bind to the address"
    },
    {
      "name": "client_secret",
      "type": "token",
      "required": true,
      "label": "Client-generated secret that links validation steps in a session"
    },
    {
      "name": "session_id",
      "type": "token",
      "required": false,
      "label": "Server-assigned identifier for the validation session"
    },
    {
      "name": "id_server",
      "type": "url",
      "required": true,
      "label": "Domain of the identity server handling this request"
    },
    {
      "name": "validated_at",
      "type": "datetime",
      "required": false,
      "label": "Timestamp when the identity server confirmed the address ownership"
    }
  ],
  "rules": {
    "access": [
      "Identity server addresses must use the HTTPS scheme; HTTP is not permitted",
      "Validation token requests are rate-limited per IP address and per third-party address",
      "The client_secret must be present in all requests that continue a validation session",
      "Third-party lookup can be disabled server-wide via configuration",
      "Binding a third-party identifier to an account requires a successfully validated session"
    ],
    "validation": [
      "Validation sessions have a time limit enforced by the identity server",
      "Identity server responses must include the medium field to be considered successful"
    ]
  },
  "outcomes": {
    "validation_token_sent": {
      "priority": 1,
      "given": [
        "third-party lookup is enabled on the server",
        "request is not rate-limited by IP or address",
        "id_server uses HTTPS scheme",
        "client_secret is provided"
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "identity.validation_requested",
          "payload": [
            "medium",
            "address",
            "session_id"
          ]
        }
      ],
      "result": "Validation message sent to the address; user must confirm receipt"
    },
    "validation_completed": {
      "priority": 2,
      "given": [
        "user has confirmed the validation token",
        "session_id and client_secret match the pending session"
      ],
      "then": [
        {
          "action": "create_record",
          "type": "validated_address",
          "target": "identity_store",
          "description": "Validated third-party identifier recorded with timestamp"
        },
        {
          "action": "emit_event",
          "event": "identity.validated",
          "payload": [
            "medium",
            "address",
            "validated_at"
          ]
        }
      ],
      "result": "Identity server confirms ownership; address may now be bound to an account"
    },
    "address_bound": {
      "priority": 3,
      "given": [
        "validation session is complete",
        "user is authenticated"
      ],
      "then": [
        {
          "action": "create_record",
          "type": "identity_binding",
          "target": "identity_server",
          "description": "Binding between address and account identifier stored on identity server"
        },
        {
          "action": "emit_event",
          "event": "identity.bound",
          "payload": [
            "user_id",
            "medium",
            "address"
          ]
        }
      ],
      "result": "Other users can invite this user by their email or phone number"
    },
    "address_lookup": {
      "priority": 4,
      "given": [
        "requester is authenticated",
        "lookup is enabled on the server",
        "id_server is reachable"
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "identity.looked_up",
          "payload": [
            "medium",
            "address",
            "resolved_user_id"
          ]
        }
      ],
      "result": "User ID associated with the address returned to requester"
    },
    "rate_limited": {
      "priority": 5,
      "error": "IDENTITY_RATE_LIMITED",
      "given": [
        {
          "any": [
            "IP address has exceeded validation token request rate",
            "address has exceeded validation token request rate"
          ]
        }
      ],
      "then": [],
      "result": "Request rejected until rate limit window resets"
    },
    "identity_server_unreachable": {
      "priority": 6,
      "error": "IDENTITY_SERVER_UNREACHABLE",
      "given": [
        "identity server does not respond within the timeout period"
      ],
      "then": [],
      "result": "Operation fails; user should retry later"
    },
    "lookup_disabled": {
      "priority": 7,
      "error": "IDENTITY_LOOKUP_DISABLED",
      "given": [
        "third-party lookup is disabled in server configuration"
      ],
      "then": [],
      "result": "Request rejected with configuration error"
    }
  },
  "errors": [
    {
      "code": "IDENTITY_RATE_LIMITED",
      "status": 429,
      "message": "Too many identity requests. Please wait before trying again."
    },
    {
      "code": "IDENTITY_SERVER_UNREACHABLE",
      "status": 500,
      "message": "Could not contact the identity server. Please try again later."
    },
    {
      "code": "IDENTITY_LOOKUP_DISABLED",
      "status": 403,
      "message": "Third-party identifier lookup is not enabled on this server"
    },
    {
      "code": "IDENTITY_SERVER_INVALID",
      "status": 400,
      "message": "Identity server address is invalid or uses an unsupported scheme"
    },
    {
      "code": "IDENTITY_SESSION_INVALID",
      "status": 400,
      "message": "The validation session could not be found or the client secret is incorrect"
    }
  ],
  "events": [
    {
      "name": "identity.validation_requested",
      "description": "A validation token was requested for a third-party address",
      "payload": [
        "medium",
        "address",
        "session_id"
      ]
    },
    {
      "name": "identity.validated",
      "description": "The user confirmed ownership of the third-party address",
      "payload": [
        "medium",
        "address",
        "validated_at"
      ]
    },
    {
      "name": "identity.bound",
      "description": "A third-party address was bound to an account identifier",
      "payload": [
        "user_id",
        "medium",
        "address"
      ]
    },
    {
      "name": "identity.looked_up",
      "description": "A third-party address was resolved to an account identifier",
      "payload": [
        "medium",
        "address",
        "resolved_user_id"
      ]
    }
  ],
  "related": [
    {
      "feature": "room-invitations",
      "type": "recommended",
      "reason": "Third-party invites use identity lookup to invite users by email or phone"
    },
    {
      "feature": "push-notification-gateway",
      "type": "recommended",
      "reason": "Email pushers verify their pushkey against the user's bound identities"
    }
  ],
  "agi": {
    "goals": [
      {
        "id": "reliable_identity_lookup",
        "description": "Bridge between user contact details (email, phone) and messaging identities via external identity servers. Enables invitations before account creation and contact binding.",
        "success_metrics": [
          {
            "metric": "unauthorized_access_rate",
            "target": "0%",
            "measurement": "Failed authorization attempts that succeed"
          },
          {
            "metric": "response_time_p95",
            "target": "< 500ms",
            "measurement": "95th percentile response time"
          }
        ],
        "constraints": [
          {
            "type": "security",
            "description": "Follow OWASP security recommendations",
            "negotiable": false
          },
          {
            "type": "security",
            "description": "Sensitive fields must be encrypted at rest and never logged in plaintext",
            "negotiable": false
          }
        ]
      }
    ],
    "autonomy": {
      "level": "supervised",
      "human_checkpoints": [
        "before modifying sensitive data fields"
      ],
      "escalation_triggers": [
        "error_rate > 5",
        "consecutive_failures > 3"
      ]
    },
    "safety": {
      "action_permissions": [
        {
          "action": "validation_token_sent",
          "permission": "autonomous"
        },
        {
          "action": "validation_completed",
          "permission": "autonomous"
        },
        {
          "action": "address_bound",
          "permission": "autonomous"
        },
        {
          "action": "address_lookup",
          "permission": "autonomous"
        },
        {
          "action": "rate_limited",
          "permission": "autonomous"
        },
        {
          "action": "identity_server_unreachable",
          "permission": "autonomous"
        },
        {
          "action": "lookup_disabled",
          "permission": "human_required"
        }
      ]
    },
    "tradeoffs": [
      {
        "prefer": "security",
        "over": "performance",
        "reason": "authentication must prioritize preventing unauthorized access"
      }
    ],
    "verification": {
      "invariants": [
        "sensitive fields are never logged in plaintext",
        "all data access is authenticated and authorized",
        "error messages never expose internal system details"
      ]
    }
  },
  "extensions": {
    "source": {
      "repo": "https://github.com/element-hq/synapse",
      "project": "Synapse Matrix homeserver",
      "tech_stack": "Python / Twisted async",
      "files_traced": 5,
      "entry_points": [
        "synapse/handlers/identity.py",
        "synapse/http/client.py"
      ]
    }
  }
}