Extensions catalog
The substrate ships eight example extensions in the binary, each a real
closure a user installs with ssctl apply or from the
catalog. Six are integrations (they connect
an external provider); two are capability bundles (they add tools and
vocabulary with no provider account). The split is the catalog's own
integration flag, curated per extension rather than inferred. Every
integration syncs from the provider into the graph; none writes back to the
provider, and each ships a README stating its limits.
This is the map. The source of truth is each bundle's own manifest under
svc/teild/substrate/examples/, and the resources an install will add are
previewable through GET …/catalog/{id}.
Type names below are the bare singular each extension declares. The group
carries the provider, so nothing repeats it: GitHub's issue record is
issue.github.bundles.teild.dev, Linear's is issue.linear.bundles.teild.dev,
and the full (group, type, id) identity is what addresses either.
| Extension | Kind | Auth | Types | Functions | Triggers | Agents |
|---|---|---|---|---|---|---|
| Integration | OAuth | 8 | 4 | 6 | 0 | |
| GitHub | Integration | OAuth | 6 | 1 | 2 | 0 |
| Linear | Integration | OAuth | 5 | 2 | 3 | 0 |
| WHOOP | Integration | OAuth | 5 | 1 | 2 | 0 |
| Notion | Integration | Internal token | 4 | 1 | 2 | 0 |
| Beeper | Integration | Pasted token | 4 | 1 | 2 | 0 |
| Firecrawl | Capability | API key | 2 | 2 | 0 | 0 |
| Web harvester | Capability | none | 2 | 4 | 4 | 3 |
Group google.bundles.teild.dev. An OAuth integration that syncs a Google
account's address book, mail and calendars into the graph and folds them onto
your people. The first real integration on the native path.
Three independent streams share one account: contacts, gmail and calendar.
Each has its own toggle, its own scope, its own function, its own pair of
triggers, and its own prefixed cadence anchor and cursor on the account, so
one stream erroring never stalls another. The account-level lastSyncedAt and
syncStatus stay shared: they are the rollup every connection reports, and
whichever stream finishes stamps them.
- Types (8):
config(the config type,bundleconfigandoauth2),account(the Connection,accountconfig),contact(the mirrored contact record),emailaddress(one address, the shared people source),threadandmessage(the Gmail mirrors),calendarandevent(the Calendar mirrors). - Functions (4):
contactssyncpagespeople/me/connections, emitscontactrecords, and stores the People sync token for incremental runs;gmailsyncdrains Gmail history (or a bounded backfill window) into thread and message mirrors plus coreemailthreadandemailmessagerows;calendarsyncdrains each calendar's events on that calendar's own sync token into event mirrors plus corecalendarandcalendareventrows;contactsidmigrationis a bounded, trigger-less callable that re-keys older ad-hoc contact ids onto the deterministic external-id scheme. - Triggers (6):
google-contacts-on-connect,google-gmail-on-connectandgoogle-calendar-on-connectfire their stream's sync the first time an account connects with that toggle on;google-contacts-scheduled,google-gmail-scheduledandgoogle-calendar-scheduledfire them hourly. - Mappings:
contacttoperson, matching on email and mapping name plus the union of emails and phones;emailaddresstoperson, matching on the address and contributing the header name plus that address.
Mirrors plus direct core emission. Provider records are mirrored in
Google's own shape, and the sync functions also emit the core vocabulary row
for the same logical object under the same derived id, with its required edges
filled in. Entitymappings resolve people and nothing else, because a mapping
cannot target emailmessage, emailthread or calendarevent: those types
would become server-assigned, and a mapping's shell mint carries no edges, so
it could not satisfy their required thread, account and calendar edges.
The emailaddress record is the bridge: the core rows reference it, and the
engine's one-hop resolution lands the stored edge on the person its mapping
resolved.
All three scopes are wired. enabledContacts maps to contacts.readonly,
enabledGmail to gmail.readonly, enabledCalendar to calendar.readonly.
The requested union is derived per authorization from the account's enabled
toggles, so turning a stream on after the grant landed needs a reconnect.
gmail.readonly is one of Google's restricted scopes: a published client needs
CASA verification, and an External plus Testing client has its refresh tokens
revoked after seven days.
What this slice does not do: no attachment bytes (metadata and the
attachment id only), no calendareventseries (every event row is a concrete
occurrence, with the series id and recurrence rules kept on the mirror), no
label type (core keeps provider label ids as plain strings), and no writeback.
GitHub
Group github.bundles.teild.dev. An OAuth integration that mirrors the code
work you are involved in and turns assigned issues into to-dos.
- Types (6):
config,account, and the mirrored recordsuser,repository,issue,pullrequest. - Functions (1):
syncwalks the connected user, reachable repositories, and the issues and pull requests you are involved in or review-requested on, one REST page per invocation with per-stage watermarks. - Triggers (2):
github-on-connectfiressynconce an account is connected and has a feature toggle on;github-scheduledfires it hourly. - Mapping:
usertoperson, matching on the public email and mapping name, login as a display name, and the union of emails.
Scopes are derived per toggle (read:user, and repo for issues and pull
requests). GitHub has no token-revocation endpoint, so the bundle declares
none.
Linear
Group linear.bundles.teild.dev. An OAuth integration that mirrors the issues
assigned to you and projects them onto jointly-owned tasks.
- Types (5):
config,account, and the mirroreduser,team,issue. - Functions (2):
issuessyncpages the viewer's assigned issues and mirrors the viewer, teams, and issues;taskprojectionprojects oneissueonto atask.tasks.teild.devrow, minting open tasks and patching the Linear-owned keys under a version check, moving status only on a real upstream open-to-done transition. - Triggers (3):
linear-issues-on-connectandlinear-issues-scheduleddriveissuessync(on connect and hourly);linear-task-projectionfirestaskprojectionon everyissuechange. - Mappings (2):
usertoperson(match on email, map names and emails), andissuetopersonon theassigneeedge (match on the assignee email).
This is the one integration that both mirrors a provider and projects into the
shipped tasks vocabulary, so a Linear issue and a hand-written task live side
by side.
WHOOP
Group whoop.bundles.teild.dev. An OAuth integration that mirrors a WHOOP
wearable's daily physiology.
- Types (5):
config,account, and the mirroredrecovery,sleep,workout. - Functions (1):
syncpages each enabled collection (recovery, sleep, workouts) over the provider's page token, one page per invocation. - Triggers (2):
whoop-on-connectandwhoop-scheduleddrivesync. - Mappings: none. The mirrors are their own subjects; there is no person to resolve.
Each feature toggle maps to its read scope plus a profile and offline scope, so a refresh token is granted. WHOOP's revocation is a manual authenticated delete the facility does not speak, so revocation is manual.
Notion
Group notion.bundles.teild.dev. An integration that mirrors the Notion pages
and databases shared with an internal integration. Authorized by an
internal-integration token, not OAuth, because the host OAuth facility does not
yet speak Notion's token exchange.
- Types (4):
config,account, and the mirroredpage,database. - Functions (1):
workspacesyncruns three resumable phases (search, blocks, links), pinned to the data-source API version, short-circuiting on the last-edited time for a delta sync. - Triggers (2):
notion-on-connectfires when pages or databases are enabled;notion-scheduledfires hourly. - Mappings: none. A Notion page mirrors as a document, not a person.
The integration token is a secret on the config singleton, origin-pinned to
Notion's API host. Only one account per tenant syncs: every other account row
is stamped syncStatus: ignored: duplicate account.
Beeper
Group beeper.bundles.teild.dev. The first non-OAuth integration: it connects
a Beeper (Matrix) homeserver with a pasted access token and mirrors bridged
rooms and messages (WhatsApp, Telegram, Signal, iMessage, and the rest). Read
only, it never sends.
- Types (4):
config,account, and the mirroredroom,message. - Functions (1):
messagessyncmakes one Matrix sync or messages call per invocation, mirrors rooms and messages, and stores the next-batch token for incremental runs. - Triggers (2):
beeper-messages-on-connectandbeeper-messages-scheduleddrivemessagessync. - Mappings: none by design. Bridge ghosts have no clean identity probe, so the README explicitly declines to map messages to conversations or senders to people.
The token is a secret on the config singleton, origin-pinned to Beeper hosts or
loopback. Only one account per tenant syncs: every other account row is stamped
syncStatus: ignored: duplicate account.
Firecrawl
Group firecrawl.bundles.teild.dev. A capability bundle, not a provider
account: web search and page scraping over the Firecrawl API, exposed as two
callables an agent binds as tools.
- Types (2):
config(holding an API key), andwebdocument(a scraped page kept as markdown). - Functions (2):
websearchis a read-only search returning title, URL, and snippet with no effects;scrapepagescrapes a page to markdown and upserts awebdocumentat the URL's deterministic id. - Triggers: none. Both functions are pure callables an agent or a client invokes directly, so the closure installs from
bundle.yamlalone.
The API key is a secret on the config, origin-pinned to Firecrawl's API host, injected only into these two functions.
Web harvester
Group web.bundles.teild.dev. A capability bundle, and the substrate's shipped
end-to-end conformance example: it proves the seven-kind primitive set composes
into a real feature (harvest URLs from a message, fetch and classify each page,
propose reading-list and weekly-digest notes) with no bespoke workflow
primitive. It is the running example these pages build on.
- Types (2):
configandpage(a harvested URL and its fetched, classified content). - Functions (4):
findurlsextracts URLs from a triggering message and mints pendingpagerecords;fetchpageturns a pending page into markdown;setclassis the classifier's write hand;stampconfigwrites the config and exists to prove the emit ceiling refuses a write outside it. - Triggers (4):
web-findurls-on-message(on a new conversation message runsfindurls),web-fetch-on-page(on a pending page runsfetchpage),web-classify-on-page(on a fetched, unclassified page runs thepageclassifieragent), andweb-rollup-weekly(a Monday schedule runs theweeklyrollupagent). - Agents (3):
pageclassifier(classifies a page and delegates to a reading-list sub-agent),readinglistagent(proposes reading-list notes), andweeklyrollup(queries the week's pages and proposes a digest). They use the seededcheap,mid, andstrongllm rows.
This is the only shipped bundle with agents, and the only one whose functions are deterministic stubs, because it exists to exercise the machinery rather than talk to a provider.
Back to extensions, or the ssctl command line.