Provider model
A Notewright instance composes exactly one notes source and one follow-up sink. Both are providers: small packages that implement a plain TypeScript contract and declare the configuration they read.
The two contracts
Section titled “The two contracts”A notes source implements NotesSourceProvider:
interface NotesSourceProvider { readonly id: string; readonly configSchema: ConfigSchema; listUpdatedNotes(since: Date, ctx: ProviderCtx): Promise<NoteSummary[]>; getNoteWithTranscript(id: string, ctx: ProviderCtx): Promise<NoteDetail>;}A follow-up sink implements FollowUpProvider:
interface FollowUpProvider { readonly id: string; readonly configSchema: ConfigSchema; createFollowUp(item: FollowUpItem, ctx: ProviderCtx): Promise<CreateFollowUpResult>;}Both are plain and Promise-shaped. No Effect types ever appear in a provider
contract.
configSchema: declaring what you read
Section titled “configSchema: declaring what you read”Each provider declares the env keys it needs, split by sensitivity and whether they are required:
type ConfigSchema = { readonly secrets: ReadonlyArray<string>; // sensitive, required (API keys) readonly vars: ReadonlyArray<string>; // non-sensitive, required readonly optionalVars?: ReadonlyArray<string>; // non-sensitive, optional (have defaults)};At runtime the Worker collects exactly those keys (across all three lists) from
env and hands them to the provider as ctx.config (a Record<string, string>).
A provider only ever sees the configuration it declared.
doctor requires every secrets and vars key to be set before deploy, but
never requires optionalVars (the provider falls back to a code default when one
is unset). Put a key in optionalVars only when omitting it is safe.
ProviderCtx
Section titled “ProviderCtx”Every provider method receives a context:
type ProviderCtx = { readonly config: Record<string, string>; // only the declared keys readonly fetch: typeof fetch; // use this for outbound HTTP readonly log: (fields: Record<string, unknown>) => void;};Signalling failure
Section titled “Signalling failure”A provider method signals failure by throwing a ProviderError with a kind:
throw new ProviderError({ message: "Upstream returned 503", kind: "transient", status: 503 });kind: "transient"is retried by the pipeline (rate limits,5xx).kind: "permanent"is not retried (bad input, auth failures).
Providers never return or throw Effect errors; the core normalizes thrown
ProviderErrors internally.
Built-in providers
Section titled “Built-in providers”| Provider | Kind | Package |
|---|---|---|
| Granola | Notes source | @notewright/granola-notes-provider |
| Linear | Follow-up sink | @notewright/linear-follow-up-provider |
| Noop | Follow-up sink | @notewright/noop-follow-up-provider |
To build your own, see Writing a provider.