Identity System & User Profiles
Cloudillo Profiles are located using a DNS-based identity system.
Each Cloudillo Identity is associated with a specific API endpoint, which
can be accessed via the “cl-o” subdomain of the identity.
For example, the API domain of the cloudillo.net identity is available at
https://cl-o.cloudillo.net/.
Retrieving a Cloudillo Profile
You can retrieve a Cloudillo profile by making an API request to the
/api/me endpoint of the identity’s API domain:
curl https://cl-o.cloudillo.net/api/meExample response:
{
"idTag": "cloudillo.net",
"name": "Cloudillo",
"type": "community",
"profilePic": "QoEYeG8TJZ2HTGhVlrtTDBpvBGOp6gfGhq4QmD6Z46w",
"keys": [
{
"keyId": "20250205",
"publicKey": "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAENSq6EZZ+xCypWNkm+0+MHIZfLX7I01wTT+SOw7DoOUOuDAWKMkhBVG+SUb9AzCxEOlmkefuEW5zmXNwmH2MphEQW/r18RDjd+Nt5nbemBsoQzsm2Wg/mUyWBsKYs1oe5"
}
]
}Profile Fields Explained
- idTag: The Identity Tag associated with the profile.
- name: The display name of the profile.
- type: Either empty for a personal profile or contains “community” for community profiles.
- profilePic: The identifier for the profile picture, which can be retrieved via the storage API.
- keys: A list of cryptographic keys associated with the profile.
Profile Picture Retrieval
The profilePic field contains an identifier that allows retrieval of the profile’s profile picture using the Cloudillo Storage API. A request to retrieve the profile picture may look like this:
curl https://cl-o.nikita.cloudillo.net/api/store/NMzE4gNI29aEkiV6Q7I1UNWh4x2gFZ7753Pl74veYtUKey Management
A profile supports multiple cryptographic keys, enabling periodic key rotation for enhanced security. Older keys can be deprecated while maintaining account integrity.
Key Structure
Each profile key contains:
pub struct ProfileKey {
key_id: String, // Identifier (e.g., "20250205")
public_key: String, // Base64-encoded P384 public key
created_at: i64, // Unix timestamp
expires_at: Option<i64>, // Optional expiration
key_type: KeyType, // Purpose of the key
}
pub enum KeyType {
Signing, // For action token signatures
VAPID, // For push notifications
// Future: Encryption, Authentication, etc.
}Cryptographic Algorithms
Cloudillo uses elliptic curve cryptography for signing:
- Curve: P384 (NIST P-384, also known as secp384r1)
- Algorithm: ES384 (ECDSA using P-384 and SHA-384)
- Key Size: 384 bits
- Signature Size: ~96 bytes
Why P384?
- Strong security (192-bit security level)
- Widely supported
- Good performance
- Standards-compliant (FIPS 186-4)
Key Rotation
Best practices for key rotation:
- Generate new key with current date as keyId
- Add to profile alongside existing keys
- Start using new key for new signatures
- Keep old keys active for verification (30-90 days)
- Mark old keys as expired after grace period
- Remove expired keys after verification period
Example key rotation timeline:
Day 0: Generate key "20250301"
Day 0: Add to profile, start signing with new key
Day 30: Mark old key "20250101" as expiring soon
Day 90: Set expires_at on old key
Day 180: Remove old key from profileKey Generation
Keys are generated during tenant bootstrap:
generate_p384_keypair()
encode_public_key()
key_id = current_date (YYYYMMDD format)
store in AuthAdapter (private key stored securely)VAPID Keys
VAPID (Voluntary Application Server Identification) keys are used for web push notifications:
pub struct VapidKey {
key_id: String,
public_key: String, // Base64-encoded
private_key: String, // Stored in AuthAdapter, never exposed
contact: String, // mailto: or https: URI
}These are separate from signing keys to isolate concerns and enable different rotation policies.
Identity Resolution
DNS-Based Discovery
Cloudillo uses DNS to discover the API endpoint for an identity:
- Identity:
alice.example.com - API Domain:
cl-o.alice.example.com - API Endpoint:
https://cl-o.alice.example.com/api/me
Cloudillo Identity Providers (CIP)
Users without their own domain can use a Cloudillo Identity Provider:
CIP Responsibilities:
- Domain management (subdomains or custom domains)
- DNS configuration
- Dynamic DNS support
- CIPs never store or access any user data, nor have any rescponsibilities about it.
Example CIP: cloudillo.net
- Provides identities like
alice.cloudillo.net - API available at
https://cl-o.alice.cloudillo.net - Users can migrate to their own domain later
Custom Domain Setup
To use your own domain with Cloudillo:
- DNS Configuration:
cl-o.alice.example.com. A <your-server-ip>
cl-o.alice.example.com. AAAA <your-server-ipv6>
app.example.com. A <your-server-ip>
app.example.com. AAAA <your-server-ipv6>- TLS Certificate: Automatic via ACME (Let’s Encrypt)
ACME_EMAIL=admin@example.com- Bootstrap: Configure tenant
BASE_ID_TAG=alice.example.com
BASE_APP_DOMAIN=app.example.com
BASE_PASSWORD=<secure-password>Profile Metadata
Storage
Profile metadata is stored in the MetaAdapter:
pub struct ProfileMetadata {
tn_id: TnId, // Internal tenant ID
id_tag: String, // Public identifier
name: String, // Display name
profile_type: ProfileType,
profile_pic: Option<String>, // File ID
bio: Option<String>,
created_at: i64,
updated_at: i64,
}
pub enum ProfileType {
Personal,
Community,
}Synchronization
Profiles can be synchronized across instances for caching:
GET https://cl-o.{remote_id_tag}/api/me
Parse JSON response
Cache locally in MetaAdapterSecurity Considerations
Public Key Infrastructure
- Public keys are publicly accessible via
/api/me - Private keys are stored securely in AuthAdapter
- No private key export - keys never leave the server
- Key verification happens on every action token
Identity Trust Model
Trust is established through:
- DNS ownership: Control of domain proves identity ownership
- Key signatures: Private key proves control of identity
- HTTPS: TLS proves server authenticity
- Action signatures: ES384 signatures prove action authenticity
Attack Mitigation
DNS Hijacking:
- Old signatures remain valid (past actions can’t be forged)
- Users can verify key changes
Key Compromise:
- Rotate immediately to new key
- Mark compromised key as expired
- Past actions become invalid
Server Compromise:
- Private keys in AuthAdapter may need HSM/vault for high-security deployments
- Separate AuthAdapter storage from other adapters
- Regular security audits
Bootstrap Process Details
When initializing a new Cloudillo instance:
1. Create tenant in MetaAdapter
tn_id = meta_adapter.create_tenant(base_id_tag)
2. Set password (if provided)
hash = bcrypt.hash(password)
auth_adapter.create_password(tn_id, hash)
3. Generate profile signing key
(public_key, private_key) = generate_p384_keypair()
key_id = current_date (YYYYMMDD format)
auth_adapter.create_profile_key(tn_id, key_id, public_key, private_key)
4. Initiate ACME certificate (if email provided)
cert = acme.request_certificate(domain, email)
auth_adapter.create_cert(domain, cert)API Reference
GET /api/me
Retrieve public profile information.
Request:
curl https://cl-o.example.com/api/meResponse (200 OK):
{
"idTag": "example.com",
"name": "Alice",
"type": "",
"profilePic": "f1~Qo...46w",
"keys": [
{
"keyId": "20250205",
"publicKey": "MHYwEAYHKoZIzj0CAQ..."
}
]
}GET /api/me/keys
Retrieve all public keys (for verification).
Request:
curl https://cl-o.example.com/api/me/keysResponse (200 OK):
{
"keys": [
{
"keyId": "20250205",
"publicKey": "MHYwEAYHKoZIzj0CAQ...",
"createdAt": 1738704000,
"expiresAt": null,
"keyType": "signing"
},
{
"keyId": "20250101",
"publicKey": "MHYwEAYHKoZIzj0CAQ...",
"createdAt": 1735689600,
"expiresAt": 1743465600,
"keyType": "signing"
}
]
}See Also
- System Architecture - Overall system design
- [Access Control](/architecture/data-layer/access-control/access - Token validation using profile keys
- [Actions](/architecture/actions-federation/actions - Action token signing with profile keys