This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/ICD/profile.md
2026-03-13 16:09:16 +00:00

3.5 KiB
Raw Blame History

User Profile & Settings

User identity, preferences, and credentials.

All endpoints require: Auth() middleware (JWT bearer token).


Get Profile

GET /profile

Auth: Authenticated user

Returns the current user's profile.

{
  "id": "uuid",
  "username": "jdoe",
  "email": "jdoe@example.com",
  "display_name": "Jane Doe",
  "role": "user",
  "avatar": "data:image/png;base64,...",
  "settings": { "theme": "dark" },
  "created_at": "2025-06-15T14:30:00Z",
  "last_login_at": "2025-06-15T14:30:00Z"
}
Field Type Notes
id string UUIDv4
username string Unique login name
email string Unique, lowercased
display_name string | null Optional friendly name
role "user" | "admin" Platform role
avatar string | null Data URI (PNG, 128×128). Omitted if unset.
settings object User preferences (theme, keybindings, etc.)
created_at string ISO 8601 timestamp
last_login_at string | null ISO 8601 timestamp. Null if never logged in.

Note: The profile endpoint returns avatar (not avatar_url) as a curated response shape. The User model uses avatar_url internally.


Update Profile

PUT /profile

Auth: Authenticated user

{
  "display_name": "New Name",
  "email": "new@example.com"
}

Both fields are optional (partial update). Returns the full profile (same shape as GET /profile).

Errors:

  • 409 — email already taken

Upload Avatar

POST /profile/avatar

Auth: Authenticated user

Content-Type: application/json

{
  "image": "data:image/png;base64,..."
}

Accepts a base64 data URI or raw base64 string. The server decodes, resizes to 128×128 PNG, and stores as a data URI. Max input: 2 MB.

Response:

{
  "avatar": "data:image/png;base64,..."
}

Errors:

  • 400 — missing image field, invalid base64, unsupported format, too large

Delete Avatar

DELETE /profile/avatar

Auth: Authenticated user

Response:

{
  "message": "avatar removed"
}

Change Password

POST /profile/password

Auth: Authenticated user (builtin auth only)

{
  "current_password": "old-pass",
  "new_password": "new-pass-min-8"
}

Validates current_password against stored bcrypt hash. new_password must be 8128 characters.

On success, the UEK (User Encryption Key) is re-wrapped with the new password so BYOK-encrypted provider keys remain accessible.

Response:

{
  "message": "password updated"
}

Errors:

  • 400 — missing fields, password too short/long
  • 401 — current password incorrect

Get Settings

GET /settings

Auth: Authenticated user

Returns user-level preferences wrapped in a settings key.

{
  "settings": {
    "theme": "dark",
    "editor_keybindings": "vim",
    "default_model": "claude-sonnet-4-5"
  }
}

This is a composite response (named key wrapping a single object), not a bare object. The settings key is always present; its value is {} if the user has never saved preferences.


Update Settings

PUT /settings

Auth: Authenticated user

{
  "theme": "light",
  "new_key": "new_value"
}

Shallow merge: incoming keys overwrite existing, unmentioned keys are preserved. Handles edge cases: SQL NULL, JSON null, and corrupted array values are all normalized to {} before merge.

Returns the updated settings (same shape as GET /settings).