Make features as packages
A feature is one package: read what it can do, install it as one decision, and take it back as one.
In the adminMake
A feature is one file that declares the blocks a piece of work needs: content types, collections of records, listings, forms, pages, menus, chatbots, agents, webhooks, and roles for the people who work in it. Quilzo checks every block by the same rules it holds that block to anywhere else, shows what the whole can do in plain words, and installs it as one decision.
What a package declares
Start from a complete example, a lab sample tracker, and edit it:
quilzo feature example > lab.json
quilzo feature check lab.json # what it would store, show, send and change; installs nothing
| Block | Becomes |
|---|---|
types | Content types, flat lists of fields (see Types) |
collections | Collections of records bound to one of the package's types |
listings | Declared queries over those records that a page can show (see Structure) |
forms | Forms with their purpose, lawful basis, notice and retention (see Forms) |
pages | Pages, added to the draft and never published by the install |
menus | Menus whose entries point at pages that exist |
chatbots | Chatbots answering from published pages (see Chatbots) |
agents | Agent declarations, answered for by the person who installs them (see Agents) |
webhooks | Endpoints told about events; each gets its signing secret at install, shown once |
roles | Roles for the people who work in the feature, over its own records, pages and agents (see Roles) |
A package has a name, a version, a title and a purpose, and a key it does not know is refused rather than ignored. It carries no code and no secrets, and grants nobody anything: a role it declares is held by nobody until somebody gives it to them.
Describe it, or start from a spreadsheet
Have a model draft a package from a sentence. The draft holds content blocks only: types, collections, listings, forms, pages, menus and chatbots. Anything that would reach another server, such as an agent, a webhook or a menu entry pointing at another site, is left out and named, and so are roles. A draft that fails the checks is shown its failures once, and nothing is installed until you install it.
quilzo feature draft "Track the lab's samples and show members which are available" -o lab.json
Or turn a CSV file or a JSON list of records into a feature. Each column's kind comes from its values: a number, a date, an email address, a link, yes or no, a choice among a few repeated values, or text. A column about people never becomes a choice, so their values stay out of the package. The rows are written beside the package and imported with it:
quilzo feature from samples.csv
quilzo feature install samples.json --rows samples.rows.json
A package carries definitions, never data. The rows are checked against the package's type before anything is installed, and when the feature waits for a second person, its rows wait with it. In the admin, Make offers both: Describe it and From a spreadsheet.
The permission card
The card is worked out from the package, never from its author's description: which records it keeps and whether their fields are about people, what its forms collect and for how long, which pages show what and to whom, what it sends and where, what each agent may do, where it reaches and whether it asks first, and what each role it declares lets somebody do.
One administrator installs a feature that stores and shows ordinary content. Two different administrators must agree when it:
- stores personal data, such as an email address, a name or a phone number;
- stores what looks like health, belief or other special-category data;
- collects what visitors type into a form;
- lists records about people on a public page;
- sends events or calls to another server;
- adds an agent that acts without asking first.
A model may draft a package. Only a person installs or approves one.
Roles for the people who work in it
A feature can declare roles for the people who work in it, over its own blocks and nothing else. The lab sample example declares two:
"roles": [
{"name": "technician", "title": "Lab technician",
"summary": "Records samples as they arrive, are checked out and are used up.",
"grants": [{"collection": "samples", "may": "write"}, {"page": "samples", "may": "read"}]},
{"name": "manager", "title": "Lab manager",
"summary": "Keeps the sample records and publishes the samples page.",
"grants": [{"collection": "samples", "may": "write"}, {"page": "samples", "may": "publish"}]}
]
| A grant names | And may | Becomes |
|---|---|---|
a collection the package declares | read or write | reader or author on /@data/NAME |
a page the package declares | read, write or publish | reader, author or publisher on the page and those under it |
an agent the package declares | run or approve | the run-agents or approve permission on /@agents/NAME |
A role cannot name anything the package did not declare, or administer anything. Its name is the feature's and its own, such as lab-samples/technician, and somebody who may grant access gives it to people (see roles in an app). An upgrade that changes a role changes what its holders may do, and needs somebody who may grant access when anybody holds it. A version that no longer declares a role, or removing the feature, takes it from everybody. quilzo feature show NAME says who holds each role.
Install, approve, roll back, remove
| To | Run |
|---|---|
| Install, or ask for a second person | quilzo feature install lab.json |
| See what is waiting | quilzo feature pending, or Approvals |
| Approve or decline, as somebody else | quilzo feature approve ID, quilzo feature decline ID |
| List and read what is installed | quilzo feature list, quilzo feature show NAME |
| Go back to the previous version | quilzo feature rollback NAME |
| Take everything it made back | quilzo feature remove NAME |
In the admin, Make takes a package file or pasted text, shows its card and every change it would make, and installs exactly what you read: the bytes are checked against the digest you were shown.
A feature owns what it made and nothing else. A block somebody made by hand, or another feature made, with a name the package also uses is a conflict, and nothing is installed. An upgrade changes what changed and removes what the new version no longer declares. Removing a feature stages the removal of its pages in the draft and keeps the records people entered.
Features from a repository
Keep a feature's package in a repository, review it in a pull request, and install it when the pull request merges. The CI job holds no secret: it exchanges the identity token its platform gives it for a short Quilzo token (see CI jobs without a stored secret), then posts the package:
| Request | Needs | Answers |
|---|---|---|
POST /features/check | A token that may read | The card, the plan, and both as Markdown for a pull request comment. Changes nothing. |
POST /features/install | A token that may install features, short or bound to a key | Installed, or waiting for a second person in Approvals with the job as the one who asked, with the same card, plan and rules as the Make screen. |
quilzo trust add lab-pr --github octo/lab --as ci:lab-review --role reader
quilzo trust add lab-main --github octo/lab --ref refs/heads/main --as ci:lab --role author
quilzo auth grant ci:lab-review reader
quilzo auth grant ci:lab author
quilzo auth grant ci:lab install-features
In the workflow, with TOKEN exchanged as in the credentials guide:
# on a pull request: show what it would do
curl -s https://admin.example/features/check -H "Authorization: Bearer $TOKEN" --data-binary @lab.json | jq -r .markdown > plan.md
gh pr comment "$PR" --body-file plan.md
# on main: install, or ask for a second person
curl -s https://admin.example/features/install -H "Authorization: Bearer $TOKEN" --data-binary @lab.json | jq -r .markdown
A job's token lasts minutes, which is what lets it install from another machine; a long-lived token needs to be bound to a key first. A package with personal data, a public form, anything sent outside or an agent acting on its own still waits for a person: merging a pull request is not the second approval.
Share a feature
Export an installed feature signed with your installation's log keys, Ed25519 and ML-DSA, and whoever installs it checks the signature against your published keys:
quilzo feature export lab-samples --sign -o lab.json
quilzo feature install lab.json --keys head.pub.json
A signed package is refused unless its signature is checked. To package something already built by hand, name its blocks:
quilzo feature capture inventory --title "Inventory" --purpose "What the lab holds" \
--type sample --collection samples --listing available_samples --page samples -o inventory.json