Setting up a company: blueprints, people and teams
Last updated October 5, 2026
On this page
- Before you start
- Create the company and its first admin
- Installing a blueprint
- From the Setup page
- From the command line
- Writing your own blueprint
- Describing the company to the Architect
- Promoting a blueprint from Governance
- Adding people and teams by hand
- Making someone an admin
- When someone who is not an admin adds a person
- What joining a team does
- Changing or removing someone
- Sign-in tokens
- The People page
- From the command line
- Your profile: Sessions & tokens
- Walkthrough: a fresh company, a new person, their first sign-in
- The company currency
- Not possible yet
- For developers
- API
- CLI
This page takes you from an empty folder to a company of your own: the first admin, departments from a blueprint or by hand, people and teams, the tokens people sign in with, and the currency amounts are shown in.
Examples use the demo's people where you can follow along in ok demo (Ada
the admin, Maya the support lead, Alice the sales rep, Eli the engineer), and
new names (Carol, Grace, Hugo) for people the demo does not have. What is not
possible yet is listed at the end.
A few words used throughout:
| Term | Meaning |
|---|---|
| Actor | Anyone in the company directory: a person, a team or an agent. Every actor has an id such as alice or team:sales. |
| Module | One part of the company's work, packaged: its records, agents, tools and pages. CRM and Support are modules. The command line and some messages call a module a pack. See Modules. |
| Seat | A person a blueprint adds. |
| Delegation | Permission for an agent to act for a person. See Delegations. |
| Event log | The record of everything that happened in the company. See Events and audit. |
Before you start#
You need the ok binary (see Quickstart with the demo company
for building it). Two rules apply to everything below.
- One state file is one company.
ok initwrites it to./.orchkernel/state.dbin the current folder. Pass--state <path>(or setOK_STATE) on every command to use another file. - Change the company from the command line only while the server is
stopped. A running server works from its own copy in memory and writes it
back over the file. A person added with
ok actor add-humanwhile the server runs is not seen by it and is erased at its next write, with no error. Once the server is running, use the web pages or the API (see For developers).
The demo company is for trying things. For a real company, start from
ok init as below, never from the demo's state file.
Create the company and its first admin#
-
In an empty folder, run:
ok init --admin rootYou see
kernel ready; admin root. Use --as root for admin commands.The folder now holds.orchkernel/state.db. -
List who is in the company:
ok --as root actor listYou see
root(a human with the roleadmin) and three built-in agents:architect,designerandschema-steward. There are no teams, no modules and no other people yet. -
Issue the admin a sign-in token for the web pages:
ok --as root token issue --for root --label "root laptop"It prints the token (
orchk_followed by 64 characters) and a line such astoken 0b3832b3 for root expires 2026-11-04.... The token is shown once; keep it. -
Start the server and sign in:
ok serve --bind 127.0.0.1:8080Open http://127.0.0.1:8080, paste the token and choose Sign in. Because the company has no modules yet, an admin lands on Setup ("Set up your company").
Notes:
ok initworks once per state file. Run it again and it is refused: "policy denied bootstrap: kernel already has humans; use onboard".- Commands that change the company need
--as <id>(orOK_AS) to say who is acting. Without it the command uses your computer's user name and fails with "unknown actor ". Commands that only list, such asok actor list,ok token listandok blueprint list, run without it.
Installing a blueprint#
A blueprint is a whole company, or one department, written as data: which modules to install, which teams exist, which people (seats) sit in them, which agents act for whom, and any extra pages. Installing one does in one step what you could do by hand, through the same checked actions, so the event log reads the same either way.
OrchKernel ships one blueprint:
| Blueprint | Modules | Teams | People | Agents acting for people |
|---|---|---|---|---|
SaaS startup (saas-startup) |
CRM, Support, Product | Sales, Support, Product, Engineering (Sales and Support get a 500.00 monthly budget, Product 300.00) | none | Sales Assistant for everyone in Sales; Support Agent for everyone in Support |
It also brings the modules' agents: Sales Assistant, Support Agent, Triage, Product Agent and Analyst Agent.
From the Setup page#
Admins only. Open Setup in the sidebar.
- Under Blueprints, find SaaS startup. The row lists its modules and teams.
- Choose Install. It reads "Installing…" for a moment, then a note says "Installed crm, support, product" and the row shows Already set up.
- Open Directory. Under Teams you see Engineering, Product, Sales and Support; under Agents, the five agents above next to the three built-in ones.
On a company with no modules yet, the row and the note show module ids ("crm, support, product") instead of names; after the install the row reads "CRM, Support, Product". This is a bug and has been reported.
A blueprint whose modules are all installed always shows Already set up, so you cannot install it twice from the page. In the demo, Setup shows SaaS startup as already set up. Someone who is not an admin sees "Only admins set up the company." and no Install button.
From the command line#
With the server stopped:
ok init --admin root --blueprint saas-startup # a new company, set up in one step
ok --as root blueprint install saas-startup # an existing company
ok blueprint list # the bundled blueprints
Either install prints what it did:
installed blueprint saas-startup: 4 teams, 0 people, packs [crm, support, product], 0 delegations, 0 views
Installing again is safe: everything already there is skipped and listed as
already present: team team:sales, already present: pack crm, and so on.
A skipped team is left as it is, budget included.
"0 delegations" is expected: SaaS startup has no people, so its Sales and Support teams have no one for the assistants to act for yet. As soon as you add someone to Sales, the Sales Assistant starts acting for them (see What joining a team does).
Only a human admin can install a blueprint. Anyone else is refused with "policy denied blueprint.install(saas-startup): only human admins install blueprints".
Writing your own blueprint#
ok blueprint install also takes a YAML file. Save this as acme.yaml; it
sets up sales and support with three people:
id: acme
name: Acme
description: Sales and support for Acme.
packs: [crm, support]
teams:
- { id: team:sales, name: Sales, lead: carol, budget: 50000 }
- { id: team:support, name: Support }
people:
- { id: carol, name: Carol Diaz, role: sales_lead, team: team:sales }
- { id: alice, name: Alice Chen, role: sales_rep, team: team:sales, reports_to: carol }
- { id: maya, name: Maya Patel, role: support_lead, team: team:support }
delegations:
- { agent: sales-assistant, to: [team:sales], authority: act_as, max_risk: high, reason: works each rep's pipeline }
ok --as root blueprint install acme.yaml
# installed blueprint acme: 2 teams, 3 people, packs [crm, support], 2 delegations, 0 views
The two delegations are the Sales Assistant acting for Carol and for Alice.
| Field | What it holds |
|---|---|
id, name, description |
What the blueprint is called. |
packs |
Bundled module ids to install, in order: crm, support, product, sales, marketing, support-ops, product-ops, tech, founder, context. |
teams |
Each team's id (written team:<name>), name, optional lead (a person's id) and optional monthly budget in cents (50000 is 500.00). |
people |
Each seat's id, name, role, and optional team, reports_to, email, expertise. A blueprint is the one place outside the API where you can give a person a display name. |
delegations |
Which agent acts for which teams or people: agent, to (a list), authority (act_as or advise), max_risk (low, medium, high), reason. A team expands to the people in it when the blueprint is installed. |
views |
Extra pages beyond what the modules ship. |
inline_packs |
A module defined inside the blueprint. The Architect writes these; you rarely need one by hand. |
The install refuses a blueprint that names an agent no module provides: a
delegation to nobody-agent stops it with "unknown actor nobody-agent", and
nothing is installed.
Describing the company to the Architect#
The Architect is a built-in agent that turns a sentence into a blueprint. It never installs anything itself: it files the blueprint as a change, and an admin promotes it. It calls a model to write the draft, so it needs one configured (see Models).
This does not work today. In the demo, in a fresh company under
ok serve, and with ok ask, the Architect's run fails at once with "No
native skill registered as architect", before any model is called. This is a
bug and has been reported. The steps below show what the screens do up to
that point.
- On Setup, under Or describe it, write what you want: "We also have a finance team. Grace leads it and Hugo reports to her."
- Choose Ask the Architect. The page "New conversation with Architect" opens with your text in the message box. Choose Ask agent.
- The conversation shows your message, then the Architect's reply "No native skill registered as architect" marked Failed.
Promoting a blueprint from Governance#
A blueprint proposed as a change (by the Architect once it works, or through
the API today, see For developers) waits under
Governance › Changes. To follow along in the demo, propose the Finance
team blueprint through the API first (the POST /changes row in the API
table), signed in as Ada.
- Open Governance and the Changes tab. Under Proposed you see "blueprint Finance team: 0 packs, 1 teams, 2 seats", the request it came from and "proposed by Ada Park". Choose content to see the blueprint itself.
- Choose Approve. A sheet "Approve this change" opens with an optional Note. Choose Approve again. A note says "Reviewed", the card reads "Ada Park approved: ", and its buttons are now Promote and Reject.
- Choose Promote. A note says "Promoted". The blueprint is installed at that moment: Grace Hall, Hugo Diaz and the Finance team are in the Directory and on People, and the change is listed under Promoted.
A blueprint that brings modules from the catalog is reviewed under the modules' rules; see Changing a skill or playbook and Modules.
Adding people and teams by hand#
There is no page for adding a person or a team yet. Use the command line with the server stopped, or the API while it runs.
ok --as root actor add-team team:ops --lead carol
ok --as root actor add-human carol --role sales_lead --team team:sales
ok --as root actor add-human alice --role sales_rep --team team:sales --reports-to carol
Each command prints the new person or team as JSON.
| Option | What it sets |
|---|---|
add-human <id> |
The person's id. It is also their sign-in id and, from the command line, their display name. Use lower case: alice. |
--role <role> |
Required. Free text in snake_case: sales_rep, support_lead. Shown in words on screen ("Sales rep"). Two roles make someone an admin: admin and data_owner. |
--team <team> |
The team they sit in, such as team:sales. One team per person. |
--reports-to <id> |
Their manager. Approvals that go "to the manager" go to this person. |
add-team <id> |
The team's id, written team:<name>. From the command line its display name is the id too. |
--lead <id> |
The team lead. Notices about the team's agents (such as a mention of one by someone it does not act for) go to the lead. |
Check what you typed: the command line does not check that the team, the
manager or the lead exist. --team team:nope, --reports-to nobody and
--lead carol before Carol exists are all accepted as written. This is a bug
and has been reported.
To give a person or team a proper display name ("Carol Diaz", "Operations"), add them through a blueprint or the API instead.
Making someone an admin#
Give them the role admin (or data_owner):
ok --as root actor add-human dana --role admin
Admins see People, Modules and Setup in the sidebar, issue tokens for others, install blueprints, set the currency, and decide the approvals the policy sends to an admin. In the demo, Ada is the only admin.
When someone who is not an admin adds a person#
The action is held for an admin. ok --as alice actor add-human dan --role x
answers "approval required: ", and the first admin finds "alice needs your
approval: actor.change(dan)" in their inbox (ok --as root inbox, or
Inbox on the web pages). Once approved (ok --as root approve <id>, or
Approve on the web pages), Dan is added. See
Inbox and approvals.
What joining a team does#
When a module is installed for a team, its agents act for everyone in that
team, including people who join later. In a company set up from SaaS startup,
adding Carol to team:sales makes the Sales Assistant act for her at once;
ok --as root log --limit 10 shows "sales-assistant acts for carol". See
Delegations for what acting for someone allows.
Changing or removing someone#
There is no edit command. Running add-human again with an existing id
replaces that person with exactly what you pass this time, without
warning. Anything you leave out is cleared: run
ok --as root actor add-human carol --role x on Carol and she loses her team
and her manager, and the Sales Assistant stops acting for her. To change one
thing, repeat every option you want to keep. This silent replacement is a bug
and has been reported.
To remove someone, offboard them:
ok --as root actor offboard carol
It prints offboarded carol. Offboarding revokes every delegation they gave,
cancels their running work and marks them inactive. They can no longer sign
in, even with a token issued afterwards: ok token issue --for carol still
prints a token, but the server refuses it. In the Directory, admins have an
Offboard button only on agents, not on people.
Sign-in tokens#
People sign in to the web pages by pasting a token. A token belongs to one person, lasts a set time, and can be revoked at any moment. The server keeps only a fingerprint of each token, so a token is shown exactly once, when it is issued.
In the demo, ok demo serve issues every person a 7-day token labeled
"ok demo serve" and prints them all; as it says when it starts, the next start
revokes those and prints new ones.
The People page#
Admins only. People lists every active person with their tokens: the label, who issued it and when, and when it expires ("issued by Ada Park Oct 5 · expires Oct 12 · in 7 days").
To give Eli a token for his laptop, signed in as Ada in the demo:
- Open People and choose Issue token on Eli Novak's row. A sheet "Issue a token for Eli Novak" opens: "It signs in as Eli Novak. Only you see it, once."
- Enter a Label ("Eli's laptop") so the token can be told apart later, and pick Valid for: 1 day, 7 days, 30 days (the default) or 90 days.
- Choose Issue token. The sheet becomes "Token for Eli Novak": "Copy it now: it is shown once. It expires Oct 12 · in 7 days." and the token with a Copy button.
- Copy it, send it to Eli privately, and choose I have copied it. Eli's row now reads "2 tokens" and lists "Eli's laptop".
To take access away:
- Revoke on one token. A sheet "Revoke?" says "Anyone signed in with this token is signed out at once." Choose Revoke; a note says "Token revoked".
- Revoke all on a person. A sheet "Revoke all?" asks "Sign Eli Novak out everywhere? Every token they hold is revoked." Choose Revoke all; a note says "Eli Novak is signed out everywhere" and the row reads "no tokens". Their tokens stop working at once.
On a phone-width screen the expiry line of each token is cut off after "expires Oct 12 ·". This is a bug and has been reported.
Someone who is not an admin and opens People sees "Issuing tokens for others is for admins. Your own tokens are on your profile."
From the command line#
ok --as root token issue --for alice --ttl 7d --label "Alice laptop"
ok --as root token list # ids, holders, expiry (admins see all)
ok --as root token revoke --id 5b99df8d # one token, by the id token list shows
ok --as root token revoke alice # every token Alice holds
--ttl takes a number and a unit: s, m, h, d or w (12h, 7d,
2w). The default is 30 days. A label can be up to 80 characters. Anyone can
issue a token for themselves; only an admin can issue one for someone else
("only admins issue tokens for others"). --scope limits a token to some
routes; see Outside agents.
Your profile: Sessions & tokens#
Click your name at the bottom of the sidebar to open your profile. It shows your role, team, whether you are an admin, and your sign-in id. Under Sessions & tokens you see your own tokens and can:
- New token: a token for another browser or a script of yours. The sheet "New sign-in token" has the same Label and Valid for fields; the token is shown once, under "Your new token".
- Revoke one token.
- Sign out everywhere: after you confirm ("Every token you hold is revoked, including the one this browser uses."), you are back on the sign-in page. You need a new token from an admin to sign in again.
Walkthrough: a fresh company, a new person, their first sign-in#
This puts the sections above together, outside the demo. Run it in an empty folder.
-
Create the company with the SaaS startup modules:
ok init --admin root --blueprint saas-startupYou see "kernel ready; admin root" and "installed blueprint saas-startup: 4 teams, 0 people, packs [crm, support, product], 0 delegations, 0 views".
-
Add Carol as sales lead and Alice as a rep who reports to her:
ok --as root actor add-human carol --role sales_lead --team team:sales ok --as root actor add-human alice --role sales_rep --team team:sales --reports-to carolEach prints the person.
ok --as root log --limit 10shows "sales-assistant acts for carol" and "sales-assistant acts for alice". -
Issue tokens for yourself and for Alice:
ok --as root token issue --for root --label "root laptop" ok --as root token issue --for alice --ttl 7d --label "Alice laptop" -
Start the server:
ok serve --bind 127.0.0.1:8080. -
In a private browser window, open http://127.0.0.1:8080 and paste Alice's token. She lands on Threads. Her profile reads Role "Sales rep", Team "Sales", Admin "No", and lists the token "Alice laptop", "issued by root". A wrong or expired token is refused with "That token wasn't recognised or has expired."
-
From now on, while the server runs, add people through the API (or stop the server first) and issue tokens from People.
The company currency#
Every amount in the workspace (budgets, spend, costs, money fields in records) is shown in one currency, USD until an admin changes it. The currency changes how amounts read, not the amounts kept: a budget of 50.00 stays 50.00 and reads €50.00 after a switch to EUR.
- As an admin, open Setup. Under Company, the Currency field shows the current code, with an example in the text underneath: "such as $1,250.00".
- Type a three-letter ISO 4217 code, such as
EUR(lower case is turned to upper case as you type). Pick one of the suggested codes or type any other. Save currency stays greyed out until the code has three letters and differs from the current one. - Choose Save currency. A note says "Amounts now show in EUR" and the example reads "such as €1,250.00".
- Check an amount elsewhere: in Directory, open the Support Agent. Its spent line now reads "€0.00 of €50.00".
The change is recorded in the event log as "company currency set to EUR (was USD)", by the admin who made it.
Other people signed in at the time do not always see the change at once. In the demo, Maya's open Directory panel went from "$0.00 of $50.00" to "€0.00 of €50.00" within two seconds when Ada switched to EUR, but stayed on € for more than 25 seconds when Ada then switched to INR, until a later change. Reloading the page always shows the current currency. This is a bug and has been reported.
A code that is not a currency (ABC) is refused: ""ABC" is not a currency
code. Use one such as USD, EUR or INR." Someone who is not an admin sees the
field on Setup greyed out, with no Save button. There is no command-line
command for the currency.
Not possible yet#
- Asking the Architect: it fails with "No native skill registered as architect" (a bug).
- Adding a person or a team from the web pages. Use the command line or the API.
- Editing a person: changing a role, team or manager means adding them again with every option (see Changing or removing someone).
- Setting a display name from the command line. Use a blueprint or the API.
- Offboarding a person from the web pages; only agents have an Offboard button.
- Renaming a team, or deleting a person or team. Offboarding deactivates.
- Setting the currency from the command line.
- More than one bundled blueprint.
For developers#
API#
All under /api, with Authorization: Bearer <token>.
| Method and path | Body | Notes |
|---|---|---|
GET /me |
{ actor, admin, unread, currency }. |
|
GET /company |
{ currency }. Anyone signed in. |
|
PUT /company |
{ currency } |
A human admin acting for no one. Any case; stored upper case. 403 denied otherwise; 422 invalid for a code ISO 4217 does not list. |
GET /actors |
Every directory entry: { actor, contract, cost_per_task_cents, open_tasks, reputation, skills, visibility }. |
|
GET /actors/:id |
{ entry, delegations_held, delegations_given, spent_cents }. |
|
POST /actors |
{ actor } |
Adds a person or team and returns it. A person: { "id": "carol", "name": "Carol Diaz", "kind": "human", "role": "operations_lead", "team": "team:ops", "reports_to": "fiona", "email": "carol@example.com" }. A team: { "id": "team:ops", "name": "Operations", "kind": "team", "lead": "carol" }. An existing id is replaced. A caller who is not an admin gets 202 needs_approval with the approval id. |
POST /actors/:id/offboard |
Offboards. { ok: true }. |
|
GET /blueprints |
{ bundled: [{ id, name, description, packs, teams, installed }], installed }. installed lists [id, name] for every blueprint installed so far, including promoted ones. |
|
POST /blueprints |
{ name } or { yaml } |
Installs a bundled blueprint, or one sent as YAML. Human admins only. Returns { blueprint, teams, people, packs, delegations, views, skipped }. |
POST /changes |
{ kind: "blueprint", blueprint, rationale } |
Proposes a blueprint as a change, as the Architect does. blueprint is the YAML fields as JSON. Then POST /changes/:id/review with { approve: true, note } and POST /changes/:id/promote. |
GET /tokens |
Token records: id, actor, kind, label, scopes, issued_at, expires_at, issued_by. Never the token. Admins see all, others their own. |
|
POST /tokens |
{ actor, ttl?, label?, scopes? } |
Issues a token; the answer is the only place it appears. ttl as on the command line, 30 days by default; 400 bad_request for a bad ttl. |
DELETE /tokens/:actor |
Revokes every token the actor holds. { revoked: n }. |
|
DELETE /tokens/id/:id |
Revokes one token. 404 when no token has that id. | |
PATCH /tokens/id/:id |
{ scopes } |
Narrows a token's scopes; widening is 422. |
The Finance team blueprint used in Promoting a blueprint from Governance, proposed as Ada in the demo:
curl -X POST http://127.0.0.1:8080/api/changes \
-H "Authorization: Bearer <Ada's token>" -H "content-type: application/json" \
-d '{"kind":"blueprint","rationale":"We also have a finance team. Grace leads it and Hugo reports to her.",
"blueprint":{"id":"finance-team","name":"Finance team","description":"A finance team",
"teams":[{"id":"team:finance","name":"Finance","lead":"grace"}],
"people":[{"id":"grace","name":"Grace Hall","role":"finance_lead","team":"team:finance"},
{"id":"hugo","name":"Hugo Diaz","role":"accountant","team":"team:finance","reports_to":"grace"}]}}'
Use the address ok demo serve printed in place of 127.0.0.1:8080.
People and teams added through the API get the trust tier sandboxed unless
the body says otherwise; the command line gives trusted. The Directory shows
the two differently. This is a bug and has been reported.
CLI#
ok init --admin <id> [--blueprint <id or file>]
ok blueprint list
ok blueprint install <id or file>
ok actor list
ok actor add-team <team:id> [--lead <id>]
ok actor add-human <id> --role <role> [--team <team:id>] [--reports-to <id>]
ok actor offboard <id>
ok inbox
ok approve <approval id> [--reject] [--note <text>]
ok token issue --for <id> [--ttl 30d] [--label <text>] [--scope policy|changes|read]
ok token list
ok token revoke [<id>] [--id <token id>]
ok token scope <token id> --scope <scope>
ok directory find --humans # people, ranked by availability and load
Every command reads and writes the state file directly: stop the server first.