Skip to content

Latest commit

 

History

History
445 lines (329 loc) · 19.5 KB

getting-started.md

File metadata and controls

445 lines (329 loc) · 19.5 KB

Getting started

Three ways in. Pick the one that matches what you want today.

You want to… Go to Takes
See the code run and read the interface 1. Run it on your laptop 10 minutes
Run OpenBases for your own team 2. Deploy your own a day, and some cloud accounts
Use a deployment that already exists 3. Run your first work 15 minutes

OpenBases is alpha. If you try and have a problem please open an issue with where it broke. That report is the most useful contribution anyone can make right now.


1. Run it on your laptop

You need Go, Node, PostgreSQL and make.

git clone https://github.com/datopian/openbases.git
cd openbases
make bootstrap                          # pinned bd/dolt/opencode/claude, each SHA-256 verified
export PATH="$PWD/.toolchain/bin:$PATH"

createdb workgraph
go run ./cmd/migrate -fresh -dsn postgres://localhost/workgraph
go run ./cmd/init -dsn postgres://localhost/workgraph \
  -org demo -org-name "Demo" -admin-email you@example.com -admin-name "You"

WG_ENV=local WG_DATABASE_URL=postgres://localhost/workgraph go run ./cmd/control-api
# the interface and /v1 are now on http://127.0.0.1:8080

What you get: the interface, the API and the MCP server against an empty organisation. What you do not get: agents. Dispatching needs a Cloudflare AI Gateway token, and a run without one is refused rather than reaching a model unbudgeted. There is no fixture work graph or sandbox cell yet, which is why make dev prints what it is waiting on instead of starting a stack.


2. Deploy your own

What you end up with

Two Hetzner machines, neither with an open inbound port:

  • a control node running control-api (interface, /v1, /mcp), PostgreSQL, the company work graph and the timers that reconcile, monitor and import cost;
  • an execution node holding one or more cells, each a Linux user with its own checkouts, Beads graph, dispatcher and agent runner.

Cloudflare sits in front of both: Access for sign-in (your identity provider, MFA), Tunnels so the nodes dial out, AI Gateway so every model call is budgeted and attributed, and R2 for backups. The picture is in the README under How it fits together.

One company per deployment. Multi-tenancy is not supported.

What you need first

Why
A Cloudflare account and a DNS zone Hostnames, Access, Tunnels, AI Gateway, R2. Use a zone you can grant DNS Write on.
An R2 bucket for OpenTofu state Turn on versioning.
A Hetzner Cloud project and an SSH key The two nodes.
A GitHub App, installed on the repositories agents will work on So the platform, not the agent, can open pull requests.
An age key For your own SOPS-encrypted secrets file.
Locally: tofu, ansible, sops, cloudflared Provisioning, configuration, secrets, reaching the nodes.
Optional: a Google Cloud project with Workspace delegation Only if you want Drive and Meet ingested. Off by default; nothing else depends on it.

Step 1: secrets

Start from the template, which names every value and holds none:

mkdir -p ~/.config/datopian-workgraph/secrets
cp infra/secrets/staging.example.yaml /tmp/mine.yaml      # fill it in
sops --encrypt --age <your-age-public-key> /tmp/mine.yaml \
  > ~/.config/datopian-workgraph/secrets/mine.enc.yaml && rm /tmp/mine.yaml

The real file lives outside the repository on purpose; infra/secrets/README.md says why. Every later command reads it through scripts/with_secrets.sh mine …, which decrypts it into one child process and never to disk.

Step 2: infrastructure (OpenTofu)

cp -r infra/tofu/envs/example infra/tofu/envs/mine
cd infra/tofu/envs/mine
cp terraform.tfvars.example terraform.tfvars    # your zone, hostnames, server types
cp backend.hcl.example backend.hcl              # your state bucket
cd ../../../..
scripts/with_secrets.sh mine scripts/tofu.sh mine plan
scripts/with_secrets.sh mine scripts/tofu.sh mine apply

This creates the two nodes, the network and firewall, the tunnels, the Access applications and the R2 buckets. infra/tofu/envs/example/README.md explains each value.

Step 3: configure the nodes (Ansible)

Copy infra/ansible/inventory/example.yml to inventory/mine.yml and set its four hostnames to the ones step 2 created. Name your cells in your copy of group_vars/example.yml.disabled; the committed cell names are Datopian's.

Then build and configure:

make release && make build-linux
cd infra/ansible
cloudflared access login ssh.<your-zone>          # one-hour token; the playbook retries through expiry
../../scripts/with_secrets.sh mine \
  ansible-playbook -i inventory/mine.yml site.yml

On an empty database the playbook migrates with -fresh: the schema and the permission model, with nobody in it. (Without it, the seed migrations would install Datopian's own organisation, people and projects; migrate_on_empty_database: seed keeps that old behaviour if you want it.) A database that already has migrations applied is migrated normally.

A full run takes about eleven minutes. The first line of a healthy run is "msg": "15 binaries ready to install"; if you see a refusal instead, the build step was skipped.

Step 4: create your organisation

On the control node:

sudo -u postgres wg-init -dsn "postgresql:///workgraph?host=/var/run/postgresql" \
  -org acme -org-name "Acme Ltd" \
  -admin-email ops@acme.example -admin-name "Dana Ops"

wg-init creates the organisation and the first administrator, refuses a second organisation, and is safe to re-run.

The admin email must be the one your identity provider asserts. No login identity is created here; the provider issues it on first sign-in and it is matched by address. Get it wrong and that administrator cannot sign in.

Step 5: check it

  1. Open https://<your-host> and sign in as the administrator.
  2. Connect a client (part 3, step 0) and ask "what needs me?".
  3. Create a project, attach one repository, and dispatch a small bead (part 3, steps 1 to 4). A pull request opened by the platform is the proof that every piece is wired.

Day-two operations (deploys on merge, backups, restores, rotating a credential) are in docs/runbooks/. Everything still missing before an outside install is routine is listed in docs/install/deployable-by-others.md.


3. Run your first work

This is the recommended way to use OpenBases day to day. You do not open a web page and click buttons; you talk to a client you already use — Claude (desktop, web, mobile), Claude Code, or Codex — and OpenBases' tools appear inside it over MCP. You describe the work in plain language, your client calls the right tool, and the platform does the rest.

The loop, end to end, is five moves:

  1. Connect the MCP server once.
  2. Create a project (it needs two owners).
  3. Attach the repositories the work touches.
  4. Plan and file the work as beads — this is free, no agent runs.
  5. Dispatch a bead — this spends money, an agent runs and opens a pull request.

Everything below shows both what to say to your client and the tool (and its arguments) that runs underneath, so you can see what is happening. You rarely type the arguments yourself — the model reads your intent and fills them in.

Host. Examples use the Datopian staging host, https://work-staging.openbases.com. Substitute your own deployment's host.


0. Connect the MCP server (once)

The connector has no password and no pasted token: your client opens your browser, you complete the normal login (Google Workspace, MFA, the same allow-list as the web interface), and Cloudflare Access issues the token. What you can see and do through the connector is exactly what you can see and do in the browser — a connector is not an elevated path (ADR-0028).

Claude Code:

claude mcp add --transport http openbases https://work-staging.openbases.com/mcp

Claude desktop / web / mobile / Cowork: Settings → Connectors → Add custom connector → URL https://work-staging.openbases.com/mcp → Save → Connect.

Codex CLI:

codex mcp add openbases --url https://work-staging.openbases.com/mcp

On a laptop with a shell, wg mcp serves the same tools over stdio, no connector and no OAuth:

wg login            # once
codex mcp add openbases --command wg --args mcp

To confirm it worked, ask your client "what needs me?" — it calls workgraph_inbox and should match the Needs you page in the browser.

Setup problems (the "Recommended" OAuth mode that does not work, hosted-client redirect allow-lists, revoking access) are all in docs/runbooks/connect-a-client.md. Come back here once "what needs me?" answers.


1. Create a project

A project is the unit of ownership and visibility. Its slug is permanent — it appears in bead labels (wg-project-<slug>) and decides who can read the work — so choose it deliberately.

Every project needs two different people as owners: a primary and a backup. The registry refuses a project where one person holds both roles, so there is no default the tool can invent. The primary owner defaults to whoever is asking; you must name the backup.

Say: "Create a project jackson-open-data called Jackson Open Data, with Daniela as the backup owner."

Underneath, that is workgraph_project_create:

Argument Required Notes
slug yes short, lowercase, permanent (e.g. jackson-open-data)
name yes human-readable (e.g. Jackson Open Data)
backup_owner yes email of a different person from the primary owner
primary_owner no defaults to the caller
portfolio no slug of the portfolio it belongs under (e.g. bizdev)
objective no one sentence on what the project is for
visibility no internal (default), public, or restricted
cell no usually omitted; the deployment's shared cell (oss) runs it. Required when visibility is restricted

Changing owners, cell, or access later

Owners are set at creation. Changing them afterwards — or assigning a project to a different execution cell, or granting a third person access — is not an MCP tool; do it in the web admin on the project page (work → the project → Manage owners / Execution cell / Grant access), or via the API:

# reassign owners (both, in one call; backup must differ from primary)
curl -X PATCH .../v1/projects/jackson-open-data -d '{"primary_owner":"a@x.com","backup_owner":"b@x.com"}'

# assign the project to an execution cell (needed before a repo becomes a rig)
curl -X PATCH .../v1/projects/jackson-open-data -d '{"cell":"oss"}'

# grant someone full access without displacing the owners (role defaults to project_lead)
curl -X POST .../v1/projects/jackson-open-data/members -d '{"email":"c@x.com"}'

These are kept off the MCP surface on purpose: they change who is accountable and who can see the work, so the person doing it should read what they are doing.


2. Attach the repositories

Work runs against code. A cell provisions one rig per attached repository, and a dispatch is routed to the rig holding the code a bead is about. A project with no repositories cannot be dispatched to.

Say: "Attach datopian/portaljs and datopian/cloud.portaljs.com to jackson-open-data."

Underneath, workgraph_repositories_attach:

Argument Notes
slug the project's slug
repositories list of owner/name — several at once; each is reported separately (attached, already_attached, or taken)

An empty repository (no commits) cannot be checked out, so a first commit is created for it and the result says so.

Note: a repository only becomes a provisioned rig once the project is assigned to a cell that runs it. Internal and open-source projects use the deployment's shared cell by default, so this is automatic. If you set a custom cell, make sure the project is assigned to it (see Changing … cell above) before you expect the repo to be picked up.


3. Plan and file the work

You plan wherever you think best — in the chat with your client, in a doc, in your head — and then file the finished plan in one call. Filing costs nothing: no agent runs, it only writes the work graph.

A plan is a list of beads. Each bead carries a ref (your own stable name for it, like scaffold or deploy), a title, acceptance criteria, and the depends_on edges to other beads. The whole plan goes in one call because a plan is a graph; filing it a bead at a time can half-fail, and a half-filed graph is worse than a refused one.

Say: "File this plan for jackson-open-data: (1) scaffold — set up the Next.js app, done when npm run build passes; (2) catalog page — list the datasets, depends on scaffold, done when the page renders the catalog from the API."

Underneath, workgraph_beads_file with project and a beads array; each bead:

Field Notes
ref required — your name for the bead; the upsert key (stable across re-plans)
title required to create; optional when closing/revising by id
acceptance what "done" means, checkable — write this; without it an agent invents its own criteria and closes against those
description the context an agent needs
design how it should be done, if you have an opinion
depends_on list of refs in this plan, or existing bead ids, that must finish first
type task, feature, bug, chore
priority 0 (highest) to 4 (lowest); omit if you do not care
id the id of an existing bead to revise or close (e.g. msf8-gru)
status set to closed to close a bead

Two things that make this safe to do often:

  • Re-filing the same plan revises those beads, it does not duplicate them — the match is on ref, and a field you leave out is left alone, not cleared. So re-plan as much as you like.
  • To close or revise a bead you only know by id (e.g. msf8-gru), give that bead an id and set status: closed. Without an id the filing matches on ref and would create a new bead — a common mistake.

The tool answers with each ref and the bead id it became, or a job id if the node is busy — pass that to workgraph_job (say "how's that job going?") to see it finish.


4. Dispatch a bead

This is the one that spends money: an agent runs against the bead in an isolated cell, does the work, and the platform (not the agent) lands the result as a pull request. Your client will prompt before running it — confirm work you did not explicitly ask to run.

Say: "Dispatch the scaffold bead" (or "dispatch wg-abc").

Underneath, workgraph_dispatch:

Argument Notes
bead the bead id (e.g. wg-abc)
rig optional — only needed if dispatch says several rigs hold this project's repositories, then name one of the rigs it listed

Only dispatch beads whose dependencies are met — a bead still blocked by an open depends_on is not ready. Ready beads for different projects can run in parallel, up to the cell's concurrency ceiling.

A refusal is a normal answer, not a fault. If it says "do not retry; this needs a different credential or a person", your role or a policy/budget stopped it — ask, do not retry. A budget refusal means the spend cap was hit. If it says the authorisation expired, reconnect the connector.


5. Watch it and read the result

Say Tool Shows
"how's that job going?" workgraph_job a queued file/dispatch job's progress, by the id it returned
"what's in flight?" workgraph_work_list what is running and what it cost
"show me bead wg-abc" workgraph_bead one bead: outcome, the agent's own words, model, cost, and the pull request
"what needs me?" workgraph_inbox decisions, risks and stalls ranked for you
"ask the chief of staff …" workgraph_ask the fixed question set over your portfolio

When the agent finishes, workgraph_bead links the pull request. Review and merge it like any other PR — the platform opened it, a human still lands it.

The acceptance check (shadow)

workgraph_bead also carries a verification object: when a bead's work lands, the platform checks the delivered change against that bead's own acceptance criteria and records a verdict — one truth value per criterion plus an overall score, each calibrated with a confidence. This runs automatically; you do not invoke it.

It uses Jev, a model that returns typed judgements instead of prose, through the AI Gateway — so it is cheap enough to run on every bead and needs no extra credential. Right now it is shadow mode: the verdict is recorded and visible, but it does not block a close or change a bead's outcome. It is there so a reviewer can see, at a glance, whether the delivery actually met what the bead asked for — and so the thresholds can be calibrated before the check ever gates anything. The design is ADR-0029.

The single best thing you can do to make it useful is to write real acceptance criteria on your beads (the acceptance field). A bead with none is recorded as unverifiable — which is itself worth knowing.


The whole loop, once more

connect ──▶ create project ──▶ attach repos ──▶ file plan (free) ──▶ dispatch (spends) ──▶ read PR
              (two owners)      (one rig each)    (beads + deps)        (agent runs)         (bead → pull request)

That is the complete first run. From here it is the same three tools over and over: file what you planned, dispatch what is ready, read what came back — and re-file to re-plan whenever the shape of the work changes.

The tool set

Ten tools, the same set over the hosted connector and over wg mcp, so nothing is available in one client and missing in another:

Read (free): workgraph_inbox, workgraph_ask, workgraph_work_list, workgraph_bead, workgraph_job, workgraph_project_list. Write (free): workgraph_beads_file, workgraph_project_create, workgraph_repositories_attach. Write (spends money): workgraph_dispatch.

Some things are deliberately not tools, and stay API calls where the person doing them reads what they are doing: detaching a repository (it withdraws the route work travels on), setting a repository's check command (it is executed on the execution node from a repo an agent can edit), and changing a project's owners, cell, or membership (they decide accountability and who can read the work). See ADR-0025 for why the tool set is curated rather than the whole API.

Related