Skip to content
Conduit

mcp server

os

An AI Operating System for small businesses, built as code your company owns and evolves.

digital-workers-ai/oscommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 25, 2026

DW-OS

An AI Operating System for small businesses, built as code your company owns and evolves.

  • Connects 34 sales, billing, support, marketing and analytics tools
  • Raw data kept as it arrived; everything rebuilt from it
  • Joins the same customer across tools, with a review queue
  • Every number traces to its tool, records and missing fields
  • Business logic in eleven plain definition files your company owns
  • Rules flag what needs attention; goals judge your targets
  • Briefings per role, written by a model from finished numbers
  • Ask questions in plain English over the same reviewed numbers
  • Search everything by words, or by meaning when enabled
  • Tracks your brand in Google and AI answers, and competitors' ads and posts
  • Any AI assistant can read it all through MCP
  • A nightly AI engineer proposes fixes; a person approves

The code in this repository is generated but is carefully reviewed by an engineer. DW-OS is distilled from a broader platform that also runs workflows; workflow support will be introduced in the future.

ci license: AGPL-3.0

Introduction

DW-OS centralizes your sales, billing, support, marketing, spreadsheets, and analytics tools, organizes what it finds, and puts your whole business behind a single pane of glass. From there it calculates your numbers, identifies what needs attention, and tracks whether you are hitting your targets.

Every number calculated can tell you which tool it came from, which records it counted, and which of those records were missing the field. Nothing is a black box, and the same data always gives the same answer.

Every day, the people who run the business can each receive a briefing written for their job: a few paragraphs on what moved, which targets are being hit, and which customers need attention now. An AI model writes those paragraphs, but is handed the finished numbers rather than the database, so it can word them but never invent them.

Every night, an AI agent reviews what changed and opens a GitHub issue for each improvement it finds, with evidence, a plan, and a way to check the result. Nothing changes until a person approves it; then a second agent builds the change in a sandbox and opens a pull request for a human to review and merge. See An engineer in your loop.

Contents

Why

A small company runs on a dozen tools and has no single place where any of it is the source of truth. Sales has one Acme, billing has another, support has a third. Nobody can say which name, which owner, or which revenue figure is the real one.

Big companies fix this by hiring a data team. A small company cannot, so it either lives with the mess or buys a dashboard. DW-OS takes a fundamentally different approach to each part of the problem.

  • Immutable raw data: Every data point from every tool is kept exactly as it arrived, and everything else is rebuilt from it.
  • Every number can be traced: Which tool, which record, how many rows went into each total, and which ones were missing the field.
  • Business as code: Your company's operating logic—what a customer is, how revenue is calculated, what counts as a problem, what your targets are—is a codebase: shaped by how your company actually works, built exactly the way you need it, and owned by you.
  • Your AI engineer keeps watch: Every night an AI agent reviews what changed, finds what is drifting, and writes up fixes with evidence. Nothing is built until you approve it.

Quickstart

Docker is all you need.

git clone https://github.com/digital-workers-ai/os.git && cd os
cp app/.env.example app/.env
docker compose -f app/docker-compose.yml up -d --build --wait
curl -X POST localhost:8092/api/sync
curl -X POST localhost:8092/api/rebuild
open http://localhost:3092

The two curl commands pull from every stand-in tool and then build every record out of what arrived; the last command opens the console on your home page; the dashboard is at http://localhost:3093, Spy at http://localhost:3094 and the studio at http://localhost:3095.

Connectors

One connector per tool. Each knows how its provider handles sign-in, how it pages through long lists, what shape its answers come back in, and which field holds the tool's own "last changed" time.

In this repository, all of them are answered by stand-ins rather than the real providers. The connector code is real and tested on every change, but this open version reads made-up data instead of a live account. Setting it up on your own tools is work we do with you: reach out.

CategoryToolsWhat comes in
Sales and CRMHubSpot, Salesforce, Google SheetsCompanies, people, deals
Billing and shopsStripe, Shopify, WooCommerceSubscriptions, orders, products, customers
SupportZendesk, IntercomTickets, organisations, people
Email and lifecycleMailchimp, Klaviyo, ActiveCampaign, SendGrid, Customer.ioCampaigns, audiences, profiles, activity
AdvertisingMeta, Google Ads, LinkedIn, Pinterest, Snapchat, TwitterAd accounts, campaigns, spend, conversions
Analytics and productGoogle Analytics, Mixpanel, Amplitude, Segment, SmartlookTraffic, events, event definitions
Meetings and messagingCalendly, Zoom, TwilioMeetings, call transcripts, messages
Competitors and visibilityGoogle Search (with AI Overviews), ChatGPT, Perplexity, Claude, Gemini, Google Ads Transparency, LinkedIn company posts, X profile posts, Instagram profile posts, TikTok profile postsBrand rank and mentions, cited sources, competitor creatives and posts

Adding a tool is one package under app/backend/app/sources/, its lines in definitions/mappings.yaml, and a saved sample response the test suite can replay. Deletions in a provider are not currently detected—a record that disappears upstream stays in the graph.

Architecture

flowchart TD

    SRC["1 · 34 connectors"]
    RAW["2 · Raw store<br/>every event as it arrived"]
    QUEUE["8 · Review queue<br/>look-alike pairs, a person confirms or rejects"]

    subgraph DET["Deterministic"]
        direction LR

        CHK["3 · Build checks<br/>the definitions must agree before anything runs"]
        MAP["4 · Mappings<br/>pick the raw fields worth keeping"]
        TRF["5 · Transforms<br/>normalize values"]
        ENT["6 · Entities and facts<br/>one per source record"]
        ER["7 · Entity resolution<br/>join on identity attributes, five guards, confirmed pairs"]
        SURV["9 · Survivorship<br/>one value per attribute: newest wins, then source priority, then a fixed order"]
        LNK["10 · Links<br/>declared relationships between entities"]
        DRV["11 · Derived facts<br/>roll-ups over one relationship"]
        CAN["12 · Canonical entities<br/>one per real thing, traced on every value"]

        MET["13 · Metrics<br/>aggregates with receipts, breakdowns, windows; some read the labeled facts"]
        RUL["14 · Rules<br/>findings with evidence"]
        GOL["15 · Goals<br/>strategies judge met · missed · unknown"]
        SNAP["16 · Snapshots<br/>the only history"]

        CHK --> MAP --> TRF --> ENT --> ER --> SURV --> LNK --> DRV --> CAN
        CAN --> MET
        CAN --> RUL
        CAN --> GOL
        MET --> SNAP
    end

    subgraph AI["AI Powered"]
        direction LR
        ENR["17 · Enrichment<br/>reads the text on any entity, writes labeled facts back onto it"]
        BRF["18 · Briefings<br/>one prompt per role, run against the numbers, targets and findings"]
        ASK["19 · Ask<br/>bounded tool loop"]
        MEAN["20 · Meaning search<br/>embeddings and reranking"]
        ENR ~~~ BRF ~~~ ASK ~~~ MEAN
    end

    subgraph SURF["Surfaces"]
        direction LR
        UI["21 · Console"]
        DASH["22 · Dashboard"]
        SPY["23 · Spy"]
        SE["24 · Search<br/>words, with a typo fallback"]
        MCP["25 · MCP"]
        UI ~~~ DASH ~~~ SPY ~~~ SE ~~~ MCP
    end

    SRC -->|sync| RAW
    RAW -->|rebuild| DET

    DET -.->|look-alike pairs| QUEUE
    QUEUE -.->|confirmed pairs| DET

    DET <-.->|any entity's text and the numbers in, labeled facts back on the entity| AI

    DET --> SURF
    AI -.-> SURF

Deterministic Layers:

  1. Connectors: One per tool, described under Connectors.
  2. Raw store: Every data point a tool ever sent, kept exactly as it arrived. Nothing here is edited or deleted, and everything below is rebuilt from it.
  3. Build checks: The definition files, explained below, are checked against each other. If they disagree, nothing runs.
  4. Mappings: A translation table between each tool's vocabulary and the business. Each line pairs a field in a tool's response with the fact it means here, so "Website" in the CRM and "domain" in support both become a company's domain. Fields without a line are left out.
  5. Transforms: A fixed list of clean-up functions for domains, emails, money, phone numbers, dates, and statuses.
  6. Entities and facts: One entity for each record each tool holds, named by the tool, the kind of thing, and the tool's own id for it. A fact has three possible states: the tool never mentioned it, the tool said it's empty, or it has a value.
  7. Entity resolution: Records are grouped on the fields the definitions say identify a thing, such as a company's domain or a person's work email.
  8. Review queue: Pairs of records whose names look alike and that share one more clue, such as a phone number or a work-email domain. A person confirms, rejects, or splits them, and each decision triggers a full rebuild.
  9. Survivorship: When tools disagree about a value, one wins per field: the most recently changed, by the tool's own clock. If two are equally recent, the tool ranked higher in the definitions wins. If still tied, a fixed order by tool name and id decides, so the answer never changes from one run to the next.
  10. Links: The connections the definitions declare, such as a subscription belonging to a company, drawn only between records that exist after resolution.
  11. Derived facts: Roll-ups across one connection—one link, one calculation, an optional filter—like a company's revenue from its subscriptions. They sit next to ordinary facts but are marked as calculated, with no tool or raw record behind them.
  12. Canonical entities: One record per real-world thing, with an id worked out from its content, so the same thing gets the same id on every rebuild.
  13. Metrics: The numbers your business runs on, defined once and calculated on request, over any date range: monthly revenue, deal count, churned subscriptions, and so on.
  14. Rules: The things worth a person's attention, written as conditions on a single record (e.g., a subscription past due, a deal past its close date, a paying customer with open tickets). Each rule carries a severity that says how quickly someone wants to know.
  15. Goals: A metric, a target a person chose, and a way to judge it: at least, at most, rising over the recorded history (which needs at least two points), or within a band around the target.
  16. Snapshots: The system's memory of its numbers. Each snapshot writes down every metric's value with a timestamp, and the series they form is what charts draw and trend goals judge. Nothing else writes history, and it is never deleted.

AI-Layered Features:

  1. Enrichment: Reads text a tool holds, such as a sales call transcript, and turns it into labeled facts on the record, picking each label from a fixed list and quoting the passage it came from.
  2. Briefings: A short written summary of the numbers, targets, and findings for one role, such as the owner or the head of sales, written by a model from a prompt file.
  3. Ask: Type a question and get an answer built from the same reviewed numbers, using seven read-only tools and at most eight rounds of tool calls.
  4. Meaning search: Finds a transcript by what it was about rather than the exact words, by turning text into vectors and reranking the results. Off until you switch it on.

Surfaces:

  1. Console: The web app: a home page with goals and findings, plus pages for activity, metrics, records, the AI parts, definitions, settings, and search.
  2. Dashboard: A second web app for readers: the pages dashboards.yaml declares, one card per metric, over any date range.
  3. Spy: A third web app: how the brand comes up in Google and AI answers for the tracked queries, and what competitors run and post.
  4. Studio: A fourth web app for whoever markets the business: a canvas of what it has made, a chat that makes more through the content skills, the calendar and the asset library.
  5. Search: One search box over everything stored: names, emails, ids, statuses, transcript passages, briefings, and the definitions themselves.
  6. MCP: The door for AI assistants. Any assistant on your machine can read the same numbers, definitions, and briefing prompts as the console, and every call is logged.

Entity Resolution

How it works:

two records, same kind          (today: people; companies declare no ladder)
          │
          ▼
  1. shared identity?  (email, domain, a corroborated tenant id)
          │ yes ──► MERGE, unless one of the five guards objects
          │ no
          ▼
  2. names look alike?  (similarity ≥ 0.8)
          │ no ────────────────────────────────────► two entities
          │ yes
          ▼
  3. a second attribute agrees?  (phone, email domain)
          │ no ────────────────────────────────────► two entities
          │ yes
          ▼
  4. REVIEW QUEUE  pair + evidence
          │
     ┌────┴─────┐
     ▼          ▼
  confirm     reject
     │          │
     ▼          ▼
  MERGE       two entities, remembered
  (survives rebuilds; each decision rebuilds on the spot;
   unmerge sends it back to 4)
     │
     ▼
  5. every rebuild re-checks the evidence
          │ still holds ────────────────────────────► keep
          │ gone ───────────────────────► keep, but flagged for a look
          │
          ▼
  6. a person measures the confirmed pattern on the test corpus
     and, if it holds, adds the attribute to identity: step 1

Your Business as Code You Own

Everything here is code your company owns: thirteen definition files that describe the business, the brand and looks folders beside them that describe how it sounds and looks, and the software underneath that connects to your tools, joins the records, calculates the numbers, writes the briefings and makes the marketing.

The definitions are where most changes happen. They hold what counts as a customer, which fields matter, how revenue is calculated, what counts as a problem, what the targets are, what questions an AI model is allowed to ask about your text, and what the studio makes and when. Every time the system starts, and every time it rebuilds, it checks the files against each other. If one file mentions a field another does not have, or a number over data nothing produces, or a target with a setting that makes no sense, the system refuses to start and says which file and which line. It will not run on definitions it knows are broken.

A business does not stand still, so the code follows it. A tool renames a field and a mapping line changes. A new status appears and a synonym line folds it in. You start caring about a number you never tracked and a metric line defines it. You sign up for a tool nobody supports yet and a connector gets written. What you run on day one is tailored to your company, and a year later it is still tailored, because it has changed every time the company did.

Because all of it lives in a repository, your business is versioned. Changing what revenue means is a proposal with a before and after, a reviewer, and a date. Months later, the history still says who changed it and when. That matters more than it sounds: when a number moves, you can tell whether the business moved or the definition did. No dashboard can answer that.

┌─────────────────┬───────────────────────────────────────────────────────────────────────────────────────┐
│      File       │                                    What it decides                                    │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ ontology.yaml   │ What kinds of thing exist, what details each one has, which details identify it,      │
│                 │ which tool wins a tie, how things connect, and what every label means                 │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ mappings.yaml   │ Which field from which tool becomes which fact here                                   │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ transforms.yaml │ How each kind of value is cleaned up, picked from a fixed list                        │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ synonyms.yaml   │ Which spellings from different tools mean the same status                             │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ derived.yaml    │ The roll-ups, like a company's revenue from its subscriptions                         │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ metrics.yaml    │ The numbers your business runs on, and how each is calculated                         │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ rules.yaml      │ What counts as worth a person's attention                                             │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ goals.yaml      │ The targets, and how each one is judged                                               │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ dashboards.yaml │ The pages of the dashboard, and which numbers each one shows                          │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ enrichment.yaml │ The questions a model may ask of your text, and the answers it may give               │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ spy.yaml        │ The brand, the competitors and the queries Spy tracks                                 │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ calendar.yaml   │ What the studio makes and when: each slot's kind, skill, cadence, look and theme      │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ prompts.yaml    │ The words a model is given before it reads your data, and which text is data          │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ brand/          │ How the company sounds and what it can prove, plus the colors, fonts and logo         │
├─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ looks/          │ The layouts an image or a carousel can take, and the slots each one fills             │
└─────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘

Briefings follow the same idea. A briefing is a few paragraphs a model writes for one job—the owner or the head of sales—saying what moved, which targets are on track, and which customers need attention. Each morning it is written fresh and each person gets a notification with theirs. What a job's briefing talks about is set by a short prompt file in plain English: what that reader cares about, in what order, at what length. One file per job, and the file is the job. Add one for the CFO and there is a CFO briefing; delete it and there is not. The prompt is versioned like everything else here, so a briefing that reads differently this month can be traced to the day someone changed the wording.

The Dashboard

The console is for whoever operates it. The dashboard is for whoever runs the business. Every card on it is a metric from metrics.yaml, calculated by the same code the console uses, and every page on it is declared in dashboards.yaml.

Adding a page is adding lines to dashboards.yaml. The same check that guards the other definition files guards this one: a card that names no metric, a metric listed twice on one page, or a metric on a ranged page that has no date to range over fails the build with a message naming the page — never a blank card. The console lists every page and card under Definitions → Dashboards, so what the dashboard shows is as reviewable as how each number is calculated.

It installs as an app on an iPhone, an Android phone or a desktop: the shell is cached so the pages open offline, the numbers never are.

Spy

Spy is a third web app, at http://localhost:3094, for whoever looks after the brand. It shows how the brand comes up in Google results, Google AI Overviews, ChatGPT, Perplexity, Claude and Gemini for a fixed list of queries, which competitors those answers name and which sites they cite, what each competitor is running in Google Ads Transparency, and what each posts on its LinkedIn company page, X profile, Instagram profile and TikTok profile. Five pages: an overview, the visibility matrix per engine, the ads, the posts, and the competitors as defined.

definitions/spy.yaml holds the brand (name, domain, aliases), the competitors (name, domain, aliases, LinkedIn slug, Google advertiser id), the queries, the country and the language. It is checked at boot and before every rebuild like the other definition files, and the console shows it under Definitions → Spy. To change what Spy tracks, edit the file and rebuild. The shipped file names real companies, Pipedrive against HubSpot, Zoho CRM and Freshsales, so the stand-ins and the saved real responses have something to find.

The Studio

The dashboard is for whoever runs the business. The studio is for whoever markets it: a fourth web app with a canvas of everything the company has made, grouped by day, and a chat that makes more of it. Nothing on the canvas came from a template. Every piece was made by a content skill, one of the five dw-* skills under .claude/skills/: a post, a newsletter, a blog post, an image or a carousel. A skill is a plain file that says what it reads, the steps it takes and the rules it keeps, and the same five skills answer over MCP, so an assistant on your machine can ask for a post the way the chat does.

The skill reads the brand files, takes every number from proof.md and every quote word for word from a call transcript or a brand file, writes down each claim and where it came from, paints a picture and renders the card over it when the piece carries one, and the result lands in the library with its files, its claims and its evidence. A run that cannot source a number is held rather than finished, and says what it was missing.

An edit is a sentence in the chat. It goes back through the same skill, which keeps everything the sentence did not name and makes a new version with a fresh image; a resize does the same at the new ratio. Every version is kept, with the note that asked for it, and a person can leave feedback on any finished piece.

The brand is files your company owns, checked at startup like the other definitions. definitions/brand/ holds what the company is (brand-brain.md), how it sounds (voice.md, language.md), who it serves and what it stands for (pillars.md, audiences.md, objections.md), what it can prove (proof.md), and tokens.yaml, the brand name, colors, fonts and logo every image reads, with the fonts and the logo themselves in assets/ beside it. definitions/calendar.yaml says what gets made and when—a weekly newsletter, two posts a week, a blog post every fortnight, a carousel a month—each slot with a theme. definitions/looks/ holds the layouts an image can take: three cards in four ratios, and a carousel.

Each morning, when MARKETER_DAILY is on, a marketer routine reads the calendar and fills every empty slot in the fortnight ahead; each asset it makes carries a badge saying so, and a person can skip a slot or run one early. Each night a taste agent reads the feedback people left and the notes they typed to get the next version, and when the same correction has shown up three times across one skill's assets it appends one line to that skill's ## Lessons and opens a pull request carrying the evidence. A person merges, or does not. Its model and brief are in agents/taste.yaml; it is off until a person switches it on, see Studio Agent Variables.

Nothing publishes. An asset exports as its files, and where it goes from there is a person's decision. The studio is off until a person sets STUDIO_ENABLED, which needs both ANTHROPIC_API_KEY and OPENAI_API_KEY; the app refuses to start with the flag on and either key missing.

open http://localhost:3095

MCP Support

Any AI assistant can use DW-OS as a tool. It gets read-only access to the same reviewed numbers the console shows, so when you ask your assistant about revenue it reports the number your system agreed on rather than guessing over raw tables. It can also read the definition files, so it can check what a number means before quoting it. The studio adds four tools that only read, assets_list, assets_read, brand_read and looks_read, and one tool per content skill, dw_linkedin_post, dw_newsletter, dw_blog, dw_image and dw_carousel, which are not read-only: each one runs the skill and lands a new asset in the library.

claude mcp add --transport http os http://localhost:3092/mcp

The Nightly AI Engineer Agent

Once a day, an AI agent reviews the whole system for what quietly rots—a field nothing reads any more, two tools spelling one status differently, a merge whose evidence has disappeared, a number that jumped for no reason, a target stuck on unknown, a rule that never fires—and writes up the smallest change that would fix each one.

It is off until a person switches it on; see Operator Variables. Once enabled, it runs as a Claude Code session in GitHub Actions (.github/workflows/operator.yml, scheduled at 06:00 UTC) with the repository checked out and last night's database copy restored beside it. It reads sync_run per source, the two newest rebuild reports, the newest payload per record (object types no mapping reads, fields nothing maps, the status vocabulary each source uses), clusters that disagree on a name or an email, candidates pending over a week, confirmed pairs whose evidence is gone, hand-made merge decisions, snapshot series that jumped or turned null, goals stuck on unknown, rules that never fire, conversation turns, agent tool calls, failed briefings, and the briefs against who is asking.

A proposal is a GitHub issue labeled operator + awaiting-approval, carrying the query, its result, and—where possible—the number before and after a trial edit, plus a plan a builder can follow without the operator and a verify step.

Nothing is built until a person applies approved or replies LGTM; then operator-build.yml implements the plan and opens the pull request that closes the issue, and a person merges. The operator never commits, never branches, and never applies approved itself. Production is never touched: it reads a restored copy in the runner's own Postgres, may edit a definition and rebuild the copy for before-and-after evidence, and the copy dies with the runner. Where that copy comes from is set under Operator Variables.

The trail of operator issues is its memory: open is pending, closed without approved is declined, and a declined subject returns only with new evidence, named. Its model and brief are in agents/operator.yaml; the rules it may not edit are in .claude/skills/dw-operator and dw-operator-build.

An Engineer in Your Loop

The nightly agent is only half of the arrangement. A write-up is not a change. It is a proposal with evidence attached, and somebody has to judge whether that evidence is any good.

That is the part we do. At Digital Workers, we read every proposal the nightly agent makes about your system before anything is built. We check that the evidence shows what the agent says it shows, that the change is the smallest one that fixes the problem, and that it did not reach for new code where a definition line would do. A proposal that does not hold up is closed with the reason written down, so the same idea cannot come back without new evidence. One that does hold up gets approved, built in a sandbox, and handed to a person to merge.

For Developers

./test.sh unit    # lint, then the full suite at 100% line and branch coverage
./test.sh e2e     # against the live mock providers
./test.sh snap    # Playwright screenshots against the committed baselines
./format.sh       # apply lint fixes and formatting

Configuration

Everything is read from the environment, and app/.env is loaded first. Every setting has a default, so an empty file runs. The three API keys are never settings, and the app refuses to start a layer whose key is missing.

VariableDefaultWhat it does
DATABASE_URLpostgresql+asyncpg://os:os@localhost:5442/osPostgres connection; compose points it at the postgres service
MOCK_BASE_URLhttp://localhost:8192Where the vendored mock providers answer
STAND_INS_ONLYfalseEvery source answers from its stand-in, whatever keys are set
SYNC_RUN_RETENTION_DAYS30Sync runs older than this are pruned
ENGINE_RUN_RETENTION200Rebuild receipts kept
CONNECTOR_MAX_PAGES500Pages a connector pulls per object type before stopping
CONNECTOR_MAX_BYTES52428800Payload bytes a connector accepts per pull
ER_BUCKET_CAP50Records per identity bucket before resolution refuses to merge it
ER_ONE_RECORD_PER_SOURCEtrueA canonical entity holds at most one record per source
ENRICHMENT_ENABLEDfalseEnriches entities with inferred metadata
ENRICHMENT_MODELclaude-sonnet-5Model for enrichment
ENRICHMENT_MAX_TOKENS8000Output cap per enrichment call
ENRICHMENT_MAX_CALLS_PER_RUN200Model calls per enrichment run
ENRICHMENT_CONCURRENCY4Enrichment calls in flight at once
COACHING_ENABLEDfalseGenerate role briefings
COACHING_MODELclaude-sonnet-5Model for briefings
COACHING_MAX_TOKENS8000Output cap per briefing
CONVERSATION_ENABLEDfalseThe ask agent
CONVERSATION_MODELclaude-sonnet-5Model for the ask agent
CONVERSATION_MAX_TOKENS8000Output cap per turn
CONVERSATION_MAX_TURNS8Tool-call rounds per question
CONVERSATION_MAX_TOOL_RESULT_CHARS16000A tool result is truncated beyond this
CONVERSATION_MAX_HISTORY_TURNS12Earlier turns replayed to the model
CONVERSATION_MAX_TURN_CHARS4000A stored turn is truncated beyond this
EMBEDDINGS_ENABLEDfalseMeaning search over transcript chunks
EMBEDDING_MODELtext-embedding-3-smallEmbedding model
EMBEDDING_DIMS1536Vector width; must match the column
EMBEDDING_BATCH100Chunks per embedding request
EMBEDDINGS_MAX_CALLS_PER_RUN200Embedding requests per run
RERANK_ENABLEDfalseRerank hybrid search results
RERANK_MODELzerank-2Reranker model
RERANK_TOP20Results sent to the reranker
SEARCH_CHUNK_CHARS1200Target size of a transcript chunk
STUDIO_ENABLEDfalseThe studio's skill runs; needs ANTHROPIC_API_KEY and OPENAI_API_KEY
STUDIO_MODELclaude-opus-5Model that runs a content skill
STUDIO_MAX_TOKENS8000Output cap per skill turn
STUDIO_MAX_TURNS16Tool-call rounds per skill run
PAINT_MODELgpt-image-2OpenAI image model that paints a picture
MEDIA_DIR/mediaWhere asset files are written; compose mounts a volume there
RENDER_URLhttp://localhost:8200Where the renderer answers; compose points it at the render service
MARKETER_DAILYfalseFill the calendar's empty slots every morning
MARKETER_HOUR6UTC hour the daily fill runs
CLOCK_PINNED_ATunsetPins the app clock at one instant
KeyNeeded by
ANTHROPIC_API_KEYReading text, briefings, asking questions, the studio's skills
OPENAI_API_KEYMeaning search, the studio's pictures
ZEROENTROPY_API_KEYReranking

A source reads its real API once every variable it names is set, and the stand-in when none is. A partial set is refused, naming what is missing. <SOURCE>_BASE_URL overrides the host.

SourceVariables
ActiveCampaignACTIVECAMPAIGN_BASE_URL, ACTIVECAMPAIGN_API_KEY
AmplitudeAMPLITUDE_API_KEY, AMPLITUDE_SECRET_KEY
CalendlyCALENDLY_ACCESS_TOKEN, CALENDLY_USER_URI
ChatGPTOPENROUTER_API_KEY
ClaudeANTHROPIC_API_KEY
GeminiOPENROUTER_API_KEY
Google Ads TransparencySERPAPI_API_KEY, OPENROUTER_API_KEY
Google SearchSERPAPI_API_KEY
HubSpotHUBSPOT_ACCESS_TOKEN
Instagram PostsBRIGHTDATA_API_KEY
IntercomINTERCOM_ACCESS_TOKEN
KlaviyoKLAVIYO_API_KEY
LinkedIn Ad LibrarySEARCHAPI_API_KEY
LinkedIn Company PostsBRIGHTDATA_API_KEY
MailchimpMAILCHIMP_API_KEY
Meta Ad LibrarySEARCHAPI_API_KEY
MixpanelMIXPANEL_SERVICE_ACCOUNT_USERNAME, MIXPANEL_SERVICE_ACCOUNT_SECRET, MIXPANEL_PROJECT_ID
PerplexityOPENROUTER_API_KEY
ShopifySHOPIFY_STORE_DOMAIN, SHOPIFY_ACCESS_TOKEN
StripeSTRIPE_API_KEY
TikTok Ads LibrarySEARCHAPI_API_KEY
TikTok PostsBRIGHTDATA_API_KEY
TwilioTWILIO_ACCOUNT_SID, TWILIO_API_KEY_SID, TWILIO_API_KEY_SECRET
TwitterTWITTER_BEARER_TOKEN, TWITTER_USER_ID
X PostsBRIGHTDATA_API_KEY

Operator Variables

The nightly agent and its builder run only when the repository variable OPERATOR_ENABLED is true and the two secrets below are set; until then both workflows are skipped. These are GitHub Actions repository variables and secrets read by .github/workflows/operator.yml and operator-build.yml, not app settings. The agent restores last night's database copy from object storage: set the S3 or the GCS pair, never both; with neither set, the run syncs the mock providers and audits that instead.

VariableKindWhat it does
OPERATOR_ENABLEDvariabletrue switches both workflows on; anything else and every run is skipped
OPERATOR_SNAPSHOT_S3variablebucket/path/to/os.dump, a pg_dump custom-format file on S3
OPERATOR_AWS_ROLE_ARNvariableRole assumed through OIDC to read it; the region is us-east-1
OPERATOR_SNAPSHOT_GCSvariablebucket/path/to/os.dump on Cloud Storage
OPERATOR_GCP_WORKLOAD_IDENTITY_PROVIDERvariableWorkload identity provider the runner authenticates through
OPERATOR_GCP_SERVICE_ACCOUNTvariableService account it impersonates to read the dump
ANTHROPIC_API_KEYsecretThe operator's model
OPERATOR_GITHUB_PATsecretToken the operator uses to open issues and the builder to open pull requests

Studio Agent Variables

The taste agent runs only when the repository variable STUDIO_AGENTS_ENABLED is true; until then .github/workflows/taste.yml is skipped. Like the operator it is a Claude Code session in GitHub Actions, scheduled at 02:00 UTC, with the repository checked out and last night's database copy restored beside it, and it reaches that copy through the operator's variables: OPERATOR_SNAPSHOT_S3 with OPERATOR_AWS_ROLE_ARN, or OPERATOR_SNAPSHOT_GCS with OPERATOR_GCP_WORKLOAD_IDENTITY_PROVIDER and OPERATOR_GCP_SERVICE_ACCOUNT, never both. With neither set the copy is empty and the night finds nothing. ANTHROPIC_API_KEY is its model and OPERATOR_GITHUB_PAT opens its pull requests, on branches under taste/.

VariableKindWhat it does
STUDIO_AGENTS_ENABLEDvariabletrue switches the taste workflow on; anything else and every run is skipped

How a Rebuild Works

A rebuild is the step that turns the raw store into everything above it.

It starts by checking the definition files against each other. Those files are read live from disk, and editing one then rebuilding is the normal way to make a change, so a typo is caught before anything is wiped.

Then it wipes the whole derived layer and recomputes it. For each record, it takes the newest payload the tool ever sent, in the order records were first seen, so the same customer gets the same id every time. From there the steps run in the order on the diagram: mappings pick the fields, transforms clean the values, entities and facts are built, records are joined, disagreements are settled, links are drawn, roll-ups are calculated, and the result is written. Four things a rebuild never touches: the raw store, the snapshots, anything the AI parts wrote, and the decisions people made in the review queue.

After the write, the search index is refilled, the look-alike pairs are regenerated, and a receipt of the run is saved with its full report. If meaning search is switched on, the vectors are refreshed last.

Contact

Running DW-OS on your own tools, need a connector for something that is not on the list, or have questions worth a conversation? Happy to help. Email: hello@hiredigitalworkers.com

License

Copyright (C) 2026 Digital Workers LLC.

DW-OS is free software under the GNU Affero General Public License v3.0, see LICENSE. You can run it, change it, and keep your changes to yourself. What the license asks is that anyone you give a modified copy to, or let use one over a network, can get its source.

A commercial license is available for companies that need to keep their changes private, or want custom development and support: hello@hiredigitalworkers.com.

Outside contributions are accepted under CLA.md.

↑↓ navigate openesc close