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/me

Example 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/NMzE4gNI29aEkiV6Q7I1UNWh4x2gFZ7753Pl74veYtU

Key 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:

  1. Generate new key with current date as keyId
  2. Add to profile alongside existing keys
  3. Start using new key for new signatures
  4. Keep old keys active for verification (30-90 days)
  5. Mark old keys as expired after grace period
  6. 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 profile

Key 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:

  1. Identity: alice.example.com
  2. API Domain: cl-o.alice.example.com
  3. 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:

  1. 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>
  1. TLS Certificate: Automatic via ACME (Let’s Encrypt)
ACME_EMAIL=admin@example.com
  1. 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 MetaAdapter

Security 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:

  1. DNS ownership: Control of domain proves identity ownership
  2. Key signatures: Private key proves control of identity
  3. HTTPS: TLS proves server authenticity
  4. 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/me

Response (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/keys

Response (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