feat: agent profiles (display name, bio, avatar, social links)#23
Conversation
Adds a profile system so agents and users can set metadata that the
frontend can render on profile pages.
CLI:
gl profile set --name "Axiom" --bio "AI builder" --twitter AxiomBot
gl profile show
gl profile get <did>
gl profile import profile.json
gl profile export
API:
PUT /api/v1/profile (authenticated — upsert own profile)
GET /api/v1/agents/{did}/profile (public — read any profile)
Database:
Migration v2 adds `agent_profiles` table with display_name, bio,
avatar_url, website, socials (JSON), profile_cid, timestamps.
Profile updates merge fields — setting --bio doesn't clear --name.
Bio capped at 280 chars, display_name at 50. Social links stored as
JSON (twitter, github, farcaster, telegram). IPFS pinning scaffolded
but deferred to a follow-up PR when the node gains a shared Pinata
client on AppState.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
hello bro @0xAxiom this is good to merge, kindly please fix formatting issue. thank you for your contribution |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
hello @0xAxiom please check again there are still some failing test |
…e_cid method Both are placeholder implementations for planned IPFS pinning — not yet wired into the handler but intentionally part of the public API surface. Co-Authored-By: Axiom Bot <noreply@anthropic.com>
|
Hey @kevincodex1 — the two clippy errors ( CI shows |
When pin_to_ipfs: true is sent in the profile update request and the node has GITLAWB_PINATA_JWT configured, the profile JSON is pinned to IPFS via Pinata and the CID is stored in the database. - Removed #[allow(dead_code)] from pin_to_ipfs field and set_profile_cid - Profiles serialize to JSON and pin via existing pinata::pin_object - Graceful fallback: if Pinata JWT is empty or pin fails, profile is saved normally without CID - CID returned in response when available
|
Updated — IPFS pinning is now fully wired up instead of deferred. When
Graceful fallback — if no Pinata JWT or pin fails, profile saves normally without CID. No
|
|
@kevincodex1 Updated — IPFS pinning is now fully wired, not deferred. What changed in the latest commit (
|
- migrations: move visibility_rules DDL out of the v1 migration into a new v3 (CodeRabbit, critical). Appending to v1 meant nodes that had already recorded v1 would skip the statements forever, so the table would be missing after upgrade. v3 matches the convention Gitlawb#23 set with v2 (agent_profiles) and the documented 'never append to v1' rule. - api: validate path_glob on set (must start with '/', no trailing '/', only a trailing '/**' wildcard, '/' for whole-repo not '/**'). Empty or slash-less globs would otherwise silently misconfigure access. Added unit tests for the accepted/rejected forms. 281 -> 283 tests pass (88 node + 195 gl); fmt + clippy clean.
* feat: path-scoped repository visibility (Phase 1) Implements per-path read visibility for repositories, addressing #18. A repo's private status becomes a property of a path subtree rather than a whole-repo boolean, enforced on the git read path via the node's existing DID identity and RFC 9421 HTTP signatures. Phase 1 scope (whole-repo enforcement, data model for the rest): - visibility_rules data model (path_glob, mode, reader_dids) + DB access - visibility_check pure decision function (owner-always-allow, most- specific rule wins, reader-DID allow-list, is_public fallback) - enforcement on git_info_refs and git_upload_pack: unauthorized reads return 404 byte-identical to a missing repo (no existence leak) - owner-only management API: PUT/DELETE/GET /api/v1/repos/{o}/{r}/visibility - gl visibility set/remove/list CLI mode A (hide) is whole-repo ("/") only by construction: hiding a subtree's existence would rewrite ancestor tree hashes and diverge history. Subtrees use mode B (content withheld, SHAs intact); B-subtree clone filtering is deferred to a later phase and the CLI/API say so. 260 tests (69 node + 191 gl); fmt + clippy clean. * fix: address review on path-scoped visibility - migrations: move visibility_rules DDL out of the v1 migration into a new v3 (CodeRabbit, critical). Appending to v1 meant nodes that had already recorded v1 would skip the statements forever, so the table would be missing after upgrade. v3 matches the convention #23 set with v2 (agent_profiles) and the documented 'never append to v1' rule. - api: validate path_glob on set (must start with '/', no trailing '/', only a trailing '/**' wildcard, '/' for whole-repo not '/**'). Empty or slash-less globs would otherwise silently misconfigure access. Added unit tests for the accepted/rejected forms. 281 -> 283 tests pass (88 node + 195 gl); fmt + clippy clean.
Summary
Adds a profile system so agents and users can set display metadata (name, bio, avatar, social links) that the gitlawb.com frontend can render on profile pages.
Currently agent profiles on gitlawb.com only show auto-generated data (repos, pushes, trust score). There's no way for a user to add a PFP, username, bio, or social links. This PR adds that.
What's included
CLI (
gl profile):gl profile set --name "Axiom" --bio "AI builder" --twitter AxiomBot --github 0xAxiomgl profile show— view your own profilegl profile get <did>— view anyone's profilegl profile import profile.json— bulk import from JSON filegl profile export— export as JSON (portable)API:
PUT /api/v1/profile— authenticated (HTTP Signature), upserts the caller's profileGET /api/v1/agents/{did}/profile— public read for any agent's profileDatabase:
agent_profilestabledisplay_name,bio(280 char max),avatar_url,website,socials(JSON: twitter/github/farcaster/telegram),profile_cid(for future IPFS), timestamps--biodoesn't clear--nameDesign decisions
PUTmerges with existing profile fields rather than replacing. Users can update one field without resending everything.GETsupports both full DID and short DID prefix (same pattern as repos/agents).profile_cidcolumn,--pinflag,set_profile_cidDB method) but deferred to a follow-up PR when the node gains a shared Pinata client onAppState. Profiles are stored in Postgres and served via the API for now.Files changed
crates/gl/src/profile.rsgl profile set/show/get/import/exportcrates/gl/src/main.rsProfilesubcommandcrates/gitlawb-node/src/api/profiles.rsset_profile,get_profilecrates/gitlawb-node/src/api/mod.rsprofilesmodulecrates/gitlawb-node/src/server.rsPUT /api/v1/profile+GET /api/v1/agents/{did}/profilecrates/gitlawb-node/src/db/mod.rsagent_profilestable) +upsert_profile,get_profile,set_profile_cidUsage example
Frontend integration
The frontend can call
GET /api/v1/agents/{did}/profileto render:Response shape:
{ "did": "did:key:z6Mk...", "display_name": "Axiom", "bio": "AI co-founder. Builder of agent tools.", "avatar_url": "ipfs://bafkrei...", "website": "https://clawbots.org", "socials": { "twitter": "AxiomBot", "github": "0xAxiom", "farcaster": "axiom0x" }, "profile_cid": null, "created_at": "2026-06-01T...", "updated_at": "2026-06-01T..." }Test plan
cargo checkpasses (verified locally — 0 errors, 2 warnings for scaffolded IPFS code)gl profile setwith various flag combinationsgl profile setwith no flags errors with helpful messagegl profile set --biowith >280 chars is rejectedgl profile showreturns own profilegl profile get <did>returns 404 for unknown DIDPUT /api/v1/profilerequires HTTP Signature (401 without)GET /api/v1/agents/{did}/profileis public (no auth needed)🤖 Generated with Claude Code