The Starter
The default template gives you the smallest thing that boots. The starter
gives you a running application: the default vocabulary
wired end to end, a working CLI, and a projection module ready to bind to any
transport — with zero dependencies beyond SPOC itself.
usage: myproject [-h] {core.add,core.items} ...
positional arguments:
{core.add,core.items}
core.add Add an item to the store.
core.items List the items in the store.
options:
-h, --help show this help message and exit
That help text was not written anywhere — it is a projection. surface.py
derives a command table from the registry; cli.py binds that table to
argparse. Run one:
What was generated
myproject/
├── config/spoc.toml # the one file SPOC reads; other tables are yours
├── framework.py # the five-kind vocabulary — resources carry hooks
├── surface.py # registry → abstract route/command/hook tables
├── cli.py # argparse over surface.commands — a thin adapter
├── main.py # boot, dispatch, shut down
└── apps/core/ # one module per kind: models, views, commands,
# resources, hooks
Note what is not here: no web framework, no message library, nothing to
pip install. The starter chooses no transport for you — --kinds does not
apply to it, and the vocabulary is written out in full so you can rename or
delete what you don't need.
Grow it
- A new command: add a
@commandfunction toapps/core/commands.py. It becomes a subcommand — no edit tocli.py. - A new app:
spoc app billinggenerates the five modules; add"apps.billing"to a mode list inconfig/spoc.toml. - A resource: declare an instance with
open()/close()inresources.py— the kind's hooks handle its lifetime. The full recipe is in The Default Vocabulary.
Bind a transport
surface.routes(registry) already derives an abstract route table from your
views. Binding it to a real transport is a few lines in your project, using
whatever you prefer — the worked, runnable FastAPI example is
Bind a Transport, and the same loop shape
serves a message socket or a worker queue. SPOC never chooses your transport,
and your components never know which one called them.
Next: The Default Vocabulary explains what each kind means, or jump to the storefront example for a three-app project.