Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-014: Portable identity bundle (me.synapse)

Date: 2026-05-25 Status: Accepted (prototype shipped in v0.3) Author: Rômulo Febasi


Context

A consultant or senior IC running across N employers / clients re-types the same person entity in every workspace and loses their professional memory (projects touched, decisions signed, expertise validated) every time an engagement ends. Glean’s Personal Graph solves this conceptually but is SaaS tied to an employer’s subscription and dies at exit. LinkedIn is coarse, public, and not typed.

The v0.3 research brief argues this is one of the few moats SaaS structurally cannot copy: the bundle belongs to the human, not the org. We need a wire format for the bundle, a clear trust model, and an explicit list of what does and does not ship in the v0.3 prototype.

Decision

Wire format: single JSON file

The bundle is a single JSON document, conventionally named me-<name>.synapse.json. Schema:

{
  "synapse_protocol": "synapse/v1",
  "kind": "identity-bundle",
  "exported_at": "2026-05-25T...",
  "person": {
    "id": "romulo-febasi",
    "title": "Rômulo Febasi",
    "frontmatter": { ... },
    "body": "..."
  },
  "attestations": [
    {
      "type": "project",
      "id": "api-pagamentos",
      "role": "tech-lead",
      "since": null,
      "until": null,
      "title": "API Pagamentos"
    }
  ],
  "signature": null
}

synapse_protocol is the version marker: bump on any breaking shape change. kind is identity-bundle (more kinds reserved for the future when we share other artifacts: expertise-bundle, curriculum-bundle).

Tar+zstd was considered and rejected: the bundle is tiny (low KB), and the user benefit of being able to jq / open it in a text editor outweighs any compression saving.

Source: the self: true person

Export reads the workspace’s person entity that has self: true in its frontmatter. Exactly one is required: zero or more than one is a clear error. Attestations are derived automatically from the person’s memberships array (project, role, optional since / until). Each attestation carries the referenced entity’s title at export time so the bundle reads naturally without re-joining to the original workspace.

Import: read-only consumption

Importing a bundle into another workspace creates / updates a person entity. Imported persons carry an imported_from_bundle frontmatter marker (timestamp) so they are visibly external. Attestations attach under attested_involvement: readable but deliberately distinct from native memberships so the consuming workspace’s graph stays unambiguous.

The import strips incoming x-ai-* frontmatter stamps before writing: those reference the source workspace’s audit chain, not the consumer’s.

Signature: reserved field

signature is always null in v0.3 and the import reports the bundle as unverified. v0.4 introduces ed25519-dalek signing:

  • syn identity keygen creates the user’s keypair under ~/.config/synapse/identity/.
  • syn identity export signs the canonical JSON (bundle minus the signature field) with the configured key.
  • syn identity import verifies against a pinned public key declared in [identity] trusted_keys = ["<base64>"].

v0.3 explicit non-goals

  • No mandatory crypto: signature field is reserved but unused.
  • No attestation revocation surface. Adding it requires either a transparency log or a re-published bundle; v0.4 picks one.
  • No Sigstore-style transparency log. Reserved for v0.5+ when the user demand for cross-employer reputation justifies the operational cost.
  • No HTTP discovery (https://me.example.com/.well-known/me.synapse). Backlog.
  • No multi-bundle merge into one identity. v0.4.

Future direction (owner-flagged, 2026-05-25)

The owner flagged the bundle as the natural home for a curriculum-like surface built from workspace activity: “syn experience add …”, auto-derived skill counts from tag frequency, exportable as a CV. Out of v0.3 scope but recorded here so the prototype’s schema stays open to that direction:

  • attestations schema is intentionally extensible (open record); future commands add skill, accomplishment, certification kinds.
  • Bundle root kind is namespaced (identity-bundle); a future curriculum-bundle can layer on top with shared schema.
  • The person.frontmatter field accepts arbitrary keys today, a future experience array fits there without a schema break.

Consequences

  • + Human owns their professional graph; survives any employment change.
  • + Bundle is inspectable in jq and any text editor: no vendor lock-in even on the artifact format.
  • + Federation with an employer workspace stays opt-in: the workspace consumes the bundle (read-only), never owns it.
  • + Curriculum / CV evolution has a documented home (see above).
  • − Without crypto in v0.3, an attacker who can write to the user’s filesystem can also forge bundles. Real threat for v0.4, the current threat model is “user is honest with themselves”.
  • − Schema versioning lives on a single synapse_protocol field. When breaking changes accumulate, importer needs to learn migration. Acceptable for the prototype; ADR follow-up if synapse/v2 arrives.

Alternatives considered

  • Tar + zstd archive. Rejected: too small to benefit from compression, kills jq ergonomics.
  • Sign every entity individually with ed25519. Rejected as v0.3, threat model does not warrant per-entity signing; bundle-level signature in v0.4 carries the necessary trust.
  • Federate a remote bundle URL instead of a file. Rejected for v0.3: local-first (BR-12.1) wins; HTTP discovery is a backlog candidate when there is real demand.
  • Store inside the workspace .synapse/. Rejected: the bundle belongs to the human across workspaces. Per-workspace .synapse/ defeats the purpose.