The Chinook SDK
Two surfaces, documented separately, because they are used by different people solving different problems and mixing them is how the wrong one gets called.
| Guide | Package | Runs in | Read it if you are |
|---|---|---|---|
[Server — ChinookBE](server.md) | @chinook/server | The tenant's own backend | Writing server code: reading rows, running jobs, sending notifications |
[Frontend — ChinookFE](client.md) | @chinook/client | The browser | Writing screens: binding entities, calling AI, uploading files |
| How it fits together | — | — | New here, and want the shape of the whole thing first |
| [Getting started](getting-started.md) | — | — | New to Chinook and want the whole path once |
| [Recipes](recipes/) | — | — | After a specific task |
The one thing to know before either
Both surfaces use the same method names for the same capability (ADR-069 D1). saveEntity is saveEntity on both; chat is chat on both. What differs is what happens underneath:
ChinookFE.data.create(…) ─▶ the app's own backend
└▶ ChinookBE.data.create(…) ─▶ Postgres, with RLS armed
ChinookFE.ai.chat(…) ─▶ either its own backend or the Chinook API directly,
depending on the capability's route (ADR-068 D2)
That is deliberate. Which surface a call goes to is a kernel decision made from the capability catalogue, not a property of how the call was written, so a capability can move between routes without a single generated app changing.
Where a name comes from
Namespaces are the spec's services — SERVICE_KEYS in @chinook/spec — plus three roots that are not services because they cannot be switched off: data, spec, health (ADR-069 D2).
No namespace spells a provider. payments.charge(), never adyen.charge(). A provider's name is data, never API (ADR-056), so the same call works whichever processor or model the spec routes it to, and neither guide will teach you a vendor's vocabulary.
Three ways a namespace can answer
| Answer | Means | What to do |
|---|---|---|
| It works | Configured and implemented | — |
not_configured | Implemented; this process was not given its dependency | Pass it to createKernel() |
not_built | The spec declares it; the kernel has not implemented it | Nothing — see HANDOFF §3 |
All three exist as objects. A missing namespace would fail as undefined is not a function, which names your call site and nothing about the cause.