Setting up a company: blueprints, people and teams

Last updated October 5, 2026

On this page

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 init writes it to ./.orchkernel/state.db in the current folder. Pass --state <path> (or set OK_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-human while 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#

  1. In an empty folder, run:

    sh
    ok init --admin root
    

    You see kernel ready; admin root. Use --as root for admin commands. The folder now holds .orchkernel/state.db.

  2. List who is in the company:

    sh
    ok --as root actor list
    

    You see root (a human with the role admin) and three built-in agents: architect, designer and schema-steward. There are no teams, no modules and no other people yet.

  3. Issue the admin a sign-in token for the web pages:

    sh
    ok --as root token issue --for root --label "root laptop"
    

    It prints the token (orchk_ followed by 64 characters) and a line such as token 0b3832b3 for root expires 2026-11-04.... The token is shown once; keep it.

  4. Start the server and sign in:

    sh
    ok serve --bind 127.0.0.1:8080
    

    Open 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 init works 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> (or OK_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 as ok actor list, ok token list and ok 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.

  1. Under Blueprints, find SaaS startup. The row lists its modules and teams.
  2. Choose Install. It reads "Installing…" for a moment, then a note says "Installed crm, support, product" and the row shows Already set up.
  3. 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:

sh
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:

text
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:

yaml
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 }
sh
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.

  1. On Setup, under Or describe it, write what you want: "We also have a finance team. Grace leads it and Hugo reports to her."
  2. Choose Ask the Architect. The page "New conversation with Architect" opens with your text in the message box. Choose Ask agent.
  3. 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.

  1. 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.
  2. 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.
  3. 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.

sh
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):

sh
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:

sh
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:

  1. 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."
  2. 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.
  3. 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.
  4. 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#

sh
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.

  1. Create the company with the SaaS startup modules:

    sh
    ok init --admin root --blueprint saas-startup
    

    You see "kernel ready; admin root" and "installed blueprint saas-startup: 4 teams, 0 people, packs [crm, support, product], 0 delegations, 0 views".

  2. Add Carol as sales lead and Alice as a rep who reports to her:

    sh
    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 prints the person. ok --as root log --limit 10 shows "sales-assistant acts for carol" and "sales-assistant acts for alice".

  3. Issue tokens for yourself and for Alice:

    sh
    ok --as root token issue --for root --label "root laptop"
    ok --as root token issue --for alice --ttl 7d --label "Alice laptop"
    
  4. Start the server: ok serve --bind 127.0.0.1:8080.

  5. 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."

  6. 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.

  1. 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".
  2. 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.
  3. Choose Save currency. A note says "Amounts now show in EUR" and the example reads "such as €1,250.00".
  4. 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:

sh
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#

sh
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.