Registry

Singleton Config Rows

How registry-wide config, theme, navigation, and DTCG data are stored as singleton database rows.

Most per-project settings a registry serves aren't a collection with many rows, they're a single upserted row per registry, keyed by a literal id: 'default' primary key. A workspace push replaces the whole row wholesale; there's no partial-merge semantics, so re-running push:all is always safe to repeat.

The singleton tables

TableHoldsPush endpointRead
handoff_registry_configThe app block from handoff.config.* — title, client, breakpoints, sort orders, etc.POST /api/registry/configGET /api/registry/config (public)
handoff_registry_themeCompiled theme CSS text, pushed as-is (no SCSS/Tailwind compilation on the registry, see Model).POST /api/registry/themeGET /api/registry/theme.css (public, compiled)
handoff_registry_appearanceStructured UI customization set via /account/appearance (logo selection, color/font overrides) — not pushed from the workspace, edited directly in the registry. Its css column is appended after the pushed theme CSS.— (edited in-app)Composited into /api/registry/theme.css
handoff_registry_navigationThe page navigation tree, derived from the workspace's pages/ structure.POST /api/registry/navigationServed via getDataProvider()
handoff_registry_dtcgThe compiled DTCG manifest + css/scss/tailwind/dtcg dist output, plus the axis-aware brand/scheme token trees, see Concepts → Tokens.POST /api/registry/dtcgGET /api/registry/dtcg (+ /download)
handoff_registry_iconsThe full icon catalog (IconCatalogEntry[]).POST /api/registry/iconsGET /api/registry/icons (+ /download)
handoff_registry_logosThe full logo set (light/dark/color/mono variants).POST /api/registry/logosGET /api/registry/logos (+ GET /api/registry/logo.svg)

handoff_registry_font is the one exception worth flagging: it's one row per font file (primary key = filename), not a true singleton, because a project can push more than one font family/weight/style combination. It's grouped with the singletons here because it follows the same "whole-file replace via push, no versioned history" model, see Concepts → Assets.

See API → /api/registry for the full endpoint reference, including a few narrower endpoints outside this table (asset ingest, image-slot specs, per-component preview management).

Why singleton rows, not versioned collections

These data types are whole-project settings, not individually-addressable entities the UI or an agent needs to look up by id; there's exactly one active theme, one active navigation tree, one active token manifest per registry at any time. Modeling them as a single upserted row keeps the push contract simple (whole-file replace) and avoids paying for change-history machinery these types don't need, unlike, say, a component, where the changelog tracks a history of pushes per entity.

updatedByUserId depends on which credential the push used

Every singleton table carries an updatedByUserId column. A push authenticated with the legacy HANDOFF_SYNC_SECRET is treated as automation and leaves it null; a push authenticated with a handoff-app login JWT carries the signing user's id (verifySyncAuth returns the token's sub), so it's attributed. It's also populated when a value is changed directly in the registry UI, e.g. handoff_registry_appearance via /account/appearance.

Auth tables aren't singleton rows

user, account, session, and the OAuth/CLI-device tables (oauth_client, cli_device_session, …) are ordinary multi-row tables managed by NextAuth and the OAuth/device flows, a different part of the schema from the config singletons above. See Auth for how those work.

On this page