Skip to content

A person's OpenProfile.md: public when switched on, corrected from every surface - #74

Merged
ralyodio merged 2 commits into
mainfrom
feat/openprofile-public
Sep 13, 2026
Merged

ralyodio merged 2 commits into
mainfrom
feat/openprofile-public

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

OutreachGraph's half of the OpenProfile rollout (Anthony, 2026-09-13: "anywhere openprofile is implemented they should be able to edit from there via api/cli/mcp/pwa/desktop", "integrate on outreachgraph.com profiles too and publish those back if public").

  • Rendering through @profullstack/openprofile (buildOpenProfile in providers); Topics one per bullet.
  • Migration 0036 openprofile_settings: public (default off), handle, overrides_json, owner and claim, updated_at. The openprofile job rewrites openprofiles and never touches this.
  • Public view: no Email, phone, Contact section or mailto bullets. GET /api/v1/people/{id}/openprofile.md is keyless with CORS when public, 404 to strangers otherwise; GET /api/v1/openprofiles?since=&limit=&cursor= lists public profiles for nichedb.dev. Suppressed people are never public (publish refuses with 409, the listing and the file drop them).
  • Editing: PUT /api/v1/people/{id}/openprofile (text/markdown whole file, or JSON overlay with public/handle); operator auth as every write, or an OpenAccess bearer with openprofile:edit whose principal matches a verified email (person_emails.verified, or a corroborated email identity) or the person's published profile URL. verifyBearer is injectable; production verifies against openaccess.logicsrc.com via @logicsrc/openaccess. POST .../openprofile/publish {public} is operator-only.
  • CLI og profile <id>, og profile edit <id> [--file] ($EDITOR otherwise), og profile publish <id> --public|--private; og openprofile kept as an alias. MCP update_openprofile, publish_openprofile; the client gained put.
  • /.well-known/openaccess.json names the openprofile:edit scope. README section added.
  • Not done: <link rel="openprofile"> on a public person page, because OutreachGraph has no public person page; the listing's page is the file itself.

Tests: apps/api/src/openprofile-public.test.ts (flip, stripping, suppression, paging, overlay round trip, markdown PUT, bearer subject checks), CLI and MCP tests extended; bun run check green (1499 tests).

🤖 Generated with Claude Code

https://claude.ai/code/session_01S7yeJUHGxA4P5N74xnsRPQ

ralyodio and others added 2 commits September 13, 2026 04:33
…ery surface

The openprofile job assembles a file and rewrites it on every run. What it
must never rewrite is what the person, or the operator for them, decided:
so those decisions live beside it (openprofile_settings, migration 0036),
as the overlay @profullstack/openprofile applies everywhere, a public
switch that is off until somebody turns it on, a handle, and who claimed
the profile and how.

Rendering moves to @profullstack/openprofile so the file is the same shape
every house app serves; Topics are one per bullet now.

Public means public to everyone, minus email, phone and a Contact section:
GET /api/v1/people/{id}/openprofile.md answers with no key and CORS, and
GET /api/v1/openprofiles lists every public profile newest change first,
paged by cursor, for nichedb.dev to pull. A suppressed person is never
public, and a private profile 404s to a stranger so nothing is learned.

Editing: PUT /api/v1/people/{id}/openprofile takes the whole file as
text/markdown or a JSON overlay (identity, headline, sections, public,
handle). The operator may, as with every other write about a person; so
may the person, carrying an OpenAccess bearer with the openprofile:edit
scope whose principal is provably them (a verified email, or the
OpenProfile.md they publish). The first edit by the person is their
claim. POST .../openprofile/publish {public} is the operator's switch.
`og profile <id> | edit | publish`, MCP update_openprofile and
publish_openprofile, and the scope in /.well-known/openaccess.json.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S7yeJUHGxA4P5N74xnsRPQ
Both blocks sit above the session guard and both stay. #73 took migration
0036, so the settings table is 0037.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S7yeJUHGxA4P5N74xnsRPQ
@ralyodio
ralyodio merged commit fdb00a2 into main Sep 13, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant