# Profile: avatar, about and timezone (contract between workers)
The global rules from `docs/m1-interfaces.md` apply: latest dependencies in root `[workspace.dependencies]`, no unwrap in non-test code, clippy `-D warnings` clean, no inline styles and no JS.
## Storage: migration `crates/forge-core/migrations/0002_profile.sql`
```sql
ALTER TABLE users ADD COLUMN about TEXT NOT NULL DEFAULT '';
ALTER TABLE users ADD COLUMN timezone TEXT NOT NULL DEFAULT 'UTC';
CREATE TABLE avatars (
user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
data BLOB NOT NULL, -- re-encoded PNG, 256x256
hash TEXT NOT NULL, -- hex sha256 of data (first 16 chars), used as ETag and as ?v=
updated_at TEXT NOT NULL
);
```
Avatars live in the DB, not on disk, so the standby replica and the backups carry them automatically. Each one is about 30–80 KB.
## forge-core
```rust
// model.rs: User gains
pub about: String, // raw markdown, at most 2000 chars
pub timezone: String, // IANA name, e.g. "Europe/Istanbul"; "UTC" by default
// store
pub async fn set_profile(&self, user_id: &str, about: &str, timezone: &str) -> Result<()>;
pub async fn set_avatar(&self, user_id: &str, png: &[u8], hash: &str) -> Result<()>; // upsert
pub async fn delete_avatar(&self, user_id: &str) -> Result<()>;
pub async fn avatar_hash(&self, user_id: &str) -> Result