A community based topic aggregation platform built on atproto

feat: Add verification system to actor lexicon

Add support for cryptographically signed verifications (phone, email, domain, etc.)
to actor profiles. This is a backwards-compatible addition.

Changes:
- Replace simple verified boolean with verifications array
- Add verification lexicon definition with signature support
- Add XRPC endpoints: requestPhone, verifyPhone, getStatus

The verification array allows multiple verification types and is
cryptographically signed by trusted services (e.g., did:web:coves.social).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+214 -14
internal
atproto
lexicon
+42 -14
internal/atproto/lexicon/social/coves/actor/profile.json
···
"accept": ["image/png", "image/jpeg", "image/webp"],
"maxSize": 2000000
},
-
"verified": {
-
"type": "boolean",
-
"default": false,
-
"description": "Whether the user has completed phone verification"
-
},
-
"verifiedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When the user was verified"
-
},
-
"verificationExpiresAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When verification expires"
+
"verifications": {
+
"type": "array",
+
"description": "Cryptographically signed verifications from trusted services",
+
"items": {
+
"type": "ref",
+
"ref": "#verification"
+
}
},
"federatedFrom": {
"type": "string",
···
"type": "string",
"maxLength": 256,
"description": "Human-readable location name"
+
}
+
}
+
},
+
"verification": {
+
"type": "object",
+
"description": "A cryptographically signed verification from a trusted service",
+
"required": ["type", "verifiedBy", "verifiedAt", "expiresAt", "signature"],
+
"properties": {
+
"type": {
+
"type": "string",
+
"knownValues": ["phone", "email", "domain", "government_id"],
+
"description": "Type of verification performed"
+
},
+
"verifiedBy": {
+
"type": "string",
+
"format": "did",
+
"description": "DID of the service that performed verification (e.g., did:web:coves.social)"
+
},
+
"verifiedAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When the verification was completed"
+
},
+
"expiresAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When this verification expires and requires renewal"
+
},
+
"signature": {
+
"type": "string",
+
"description": "Base64-encoded signature over verification data (type + verifiedBy + verifiedAt + expiresAt + subject DID)"
+
},
+
"metadata": {
+
"type": "object",
+
"description": "Optional verification-specific metadata (e.g., phone country code, domain name)"
}
}
}
+41
internal/atproto/lexicon/social/coves/verification/getStatus.json
···
+
{
+
"lexicon": 1,
+
"id": "social.coves.verification.getStatus",
+
"defs": {
+
"main": {
+
"type": "query",
+
"description": "Get phone verification status for the authenticated user.",
+
"parameters": {
+
"type": "params",
+
"properties": {}
+
},
+
"output": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["hasVerifiedPhone"],
+
"properties": {
+
"hasVerifiedPhone": {
+
"type": "boolean",
+
"description": "Whether the user has a verified phone"
+
},
+
"verifiedAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When phone was verified"
+
},
+
"expiresAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When verification expires"
+
},
+
"needsRenewal": {
+
"type": "boolean",
+
"description": "Whether verification is expiring soon (within 30 days)"
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+59
internal/atproto/lexicon/social/coves/verification/requestPhone.json
···
+
{
+
"lexicon": 1,
+
"id": "social.coves.verification.requestPhone",
+
"defs": {
+
"main": {
+
"type": "procedure",
+
"description": "Request a phone verification OTP code. Rate limited to prevent abuse.",
+
"input": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["phoneNumber"],
+
"properties": {
+
"phoneNumber": {
+
"type": "string",
+
"description": "Phone number in E.164 format (e.g., +14155552671)"
+
}
+
}
+
}
+
},
+
"output": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["requestId", "expiresAt"],
+
"properties": {
+
"requestId": {
+
"type": "string",
+
"description": "Unique identifier for this verification request"
+
},
+
"expiresAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When the OTP code expires"
+
}
+
}
+
}
+
},
+
"errors": [
+
{
+
"name": "InvalidPhoneNumber",
+
"description": "Phone number format is invalid"
+
},
+
{
+
"name": "PhoneAlreadyVerified",
+
"description": "This phone number is already verified by another account"
+
},
+
{
+
"name": "RateLimitExceeded",
+
"description": "Too many verification requests. Please try again later."
+
},
+
{
+
"name": "SMSDeliveryFailed",
+
"description": "Failed to send SMS to the provided number"
+
}
+
]
+
}
+
}
+
}
+72
internal/atproto/lexicon/social/coves/verification/verifyPhone.json
···
+
{
+
"lexicon": 1,
+
"id": "social.coves.verification.verifyPhone",
+
"defs": {
+
"main": {
+
"type": "procedure",
+
"description": "Verify phone number with OTP code. On success, writes signed verification to user's PDS profile.",
+
"input": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["requestId", "code"],
+
"properties": {
+
"requestId": {
+
"type": "string",
+
"description": "Request ID from requestPhone call"
+
},
+
"code": {
+
"type": "string",
+
"description": "6-digit OTP code received via SMS"
+
}
+
}
+
}
+
},
+
"output": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": ["verified", "verifiedAt", "expiresAt"],
+
"properties": {
+
"verified": {
+
"type": "boolean",
+
"description": "Whether verification was successful"
+
},
+
"verifiedAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When the verification completed"
+
},
+
"expiresAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "When verification expires (1 year from verifiedAt)"
+
}
+
}
+
}
+
},
+
"errors": [
+
{
+
"name": "InvalidCode",
+
"description": "The provided OTP code is incorrect"
+
},
+
{
+
"name": "CodeExpired",
+
"description": "The OTP code has expired. Request a new one."
+
},
+
{
+
"name": "RequestNotFound",
+
"description": "Invalid or expired request ID"
+
},
+
{
+
"name": "TooManyAttempts",
+
"description": "Too many failed verification attempts. Request a new code."
+
},
+
{
+
"name": "PDSWriteFailed",
+
"description": "Failed to write verification to user's PDS profile"
+
}
+
]
+
}
+
}
+
}