Skip to main content

Charter Schema Reference

A charter defines exactly what a worker can do, must ask about, and must never do. It is the core governance document for every Nooterra worker.

Schema Version

Current schema version: "1.0"

Field Reference

Top-Level Fields

FieldTypeRequiredDefaultDescription
schemaVersionstringNo"1.0"Charter schema version
namestringYes""Worker name. Validation fails if empty.
purposestringYes""What this worker does (one sentence). Validation fails if empty.
canDostring[]No[]Actions the worker can take autonomously
askFirststring[]No[]Actions that require human approval before execution
neverDostring[]No[]Hard restrictions — worker will refuse these
budgetBudget | nullNonullSpending limits
scheduleSchedule | nullNonullWhen the worker runs
notificationsNotificationsNoSee belowHow to alert the human
capabilitiesCapability[]Yes[]Tools/services the worker can use. Validation fails if empty.
createdAtstring (ISO 8601)NoAuto-generatedCreation timestamp
updatedAtstring (ISO 8601)NoAuto-generatedLast update timestamp

Budget

FieldTypeRequiredDefaultDescription
amountnumberYesMaximum spend. Must be >= 0.
currencystringNo"USD"Currency code
periodstringNo"monthly"Budget period ("monthly", "weekly", "daily", etc.)
approvalThresholdnumberNoSame as amountSingle-spend amount above which approval is required

Schedule

FieldTypeRequiredDefaultDescription
typestringYesOne of: "continuous", "interval", "cron", "trigger"
valuestring | nullYesSchedule value (see below)
timezonestringNo"UTC"IANA timezone string
Schedule types:
TypeValueMeaning
continuousnullRuns 24/7
interval"1h", "5m", etc.Runs at fixed intervals
cron"0 9 * * *"Standard cron expression
trigger"on_demand"Runs only when triggered manually

Notifications

FieldTypeRequiredDefaultDescription
channelsstring[]No[]Notification channel IDs
eventsstring[]No["approval_needed", "task_complete", "error"]Events that trigger notifications

Capability

FieldTypeRequiredDefaultDescription
idstringYesCapability ID from the registry (e.g., "browser", "slack")
namestringYesHuman-readable capability name
configobjectNo{}Capability-specific configuration
summarystringNoAuto-generatedHuman-readable summary (generated by getCapabilitySummary)

Validation Rules

The validateCharter() function enforces:
  1. name must be a non-empty string
  2. purpose must be a non-empty string
  3. capabilities must contain at least one entry
  4. budget.amount must not be negative (if budget is set)
Returns { valid: boolean, errors: string[] }.

Rule Inference

When you describe a task in natural language, Nooterra infers canDo, askFirst, and neverDo rules from the task description and selected capabilities. This is done by inferCharterRules(taskDescription, capabilities).

Capability-Based Inference

Rules are inferred per capability:
CapabilitycanDoaskFirstneverDo
browserBrowse websites, search the web, extract content, use web_fetch and web_search toolsFill forms or submit data
slackRead/send messages to allowed channelsSend direct messagesPost to channels not in allowed list
emailRead emails matching criteriaSend emails (if description mentions send/reply/forward)Delete emails permanently, share email content externally
githubRead repo contents, create/update issuesCreate pull requests, merge pull requestsDelete branches/repos, modify repo settings
filesystemRead files in allowed dirs; write files (if description mentions write/create/save)Access outside allowed dirs, delete without instruction
terminalExecute shell commandsExecute destructive commands (rm -rf, drop), modify system config
stripeProcess payments, issue refunds, modify subscriptionsDelete customer data
postgres, sqliteExecute read queriesExecute write queries (INSERT, UPDATE)Execute destructive queries (DROP, TRUNCATE, DELETE without WHERE)
calendarRead calendar eventsCreate or modify eventsDelete events without confirmation
shopifyRead product/order infoModify inventory, fulfill ordersDelete products
webSearchSearch the web, use web_search tool
Any capability with requiresApproval: true in the registry automatically gets an askFirst rule.

Description-Based Inference

Additional rules are inferred from keywords in the task description:
KeywordsInferred Rules
monitor, watch, track, alertcanDo: Monitor data sources continuously, send alerts
price, cost, budget, spendaskFirst: Make purchases above threshold; neverDo: Exceed budget limits
automat, auto-askFirst: Take actions with irreversible consequences

Schedule Inference

The inferSchedule(taskDescription) function infers schedule from description keywords:
KeywordsInferred Schedule
continuous, always, 24/7, forever, constantly{ type: "continuous", value: null }
every N hours{ type: "interval", value: "Nh" }
every N minutes{ type: "interval", value: "Nm" }
hourly, every hour{ type: "interval", value: "1h" }
daily, every day, once a day{ type: "cron", value: "0 9 * * *" }
weekly, every week, once a week{ type: "cron", value: "0 9 * * 1" }
monitor, watch, track, alert, check, scan{ type: "interval", value: "1h" }
morning, every morning{ type: "cron", value: "0 8 * * *" }
when, if, trigger, on{ type: "trigger", value: "on_demand" }
(default){ type: "trigger", value: "on_demand" }

Charter Enforcement at Runtime

During execution, every tool call is classified against the charter by classifyAction() in the worker daemon:
  1. Safe tools (web_fetch, web_search, read_file, send_notification, __save_memory) are always canDo — no approval needed.
  2. neverDo rules are checked first (strictest). If all keywords in a rule appear in the action description, the action is blocked.
  3. askFirst rules are checked next. If matched, the action is paused pending approval.
  4. canDo rules are checked. If matched, the action proceeds.
  5. If the tool name contains a connected capability ID, it is treated as canDo.
  6. Default: if no rule matches, the action defaults to askFirst (fail-safe).
Keyword matching is case-insensitive. Every word in a rule must appear somewhere in "toolName argsJSON" for the rule to match.

Complete Example Charter

{
  "schemaVersion": "1.0",
  "name": "Price Monitor",
  "purpose": "Monitor competitor prices every hour and alert on Slack when changes are detected",
  "canDo": [
    "Browse websites and fetch web pages",
    "Search the web for information",
    "Extract content from pages",
    "Use web_fetch and web_search tools freely",
    "Read messages from allowed channels",
    "Send messages to allowed channels",
    "Monitor specified data sources continuously",
    "Send alerts when conditions are met"
  ],
  "askFirst": [
    "Fill forms or submit data on websites",
    "Send direct messages to individuals",
    "Make purchases above threshold"
  ],
  "neverDo": [
    "Post to channels not in the allowed list",
    "Exceed budget limits"
  ],
  "budget": {
    "amount": 100,
    "currency": "USD",
    "period": "monthly",
    "approvalThreshold": 50
  },
  "schedule": {
    "type": "interval",
    "value": "1h",
    "timezone": "America/New_York"
  },
  "notifications": {
    "channels": ["slack:#price-alerts"],
    "events": ["approval_needed", "task_complete", "error"]
  },
  "capabilities": [
    {
      "id": "browser",
      "name": "Web Browser",
      "config": {},
      "summary": "Web Browser"
    },
    {
      "id": "slack",
      "name": "Slack",
      "config": { "channels": "#price-alerts" },
      "summary": "Slack (#price-alerts)"
    },
    {
      "id": "webSearch",
      "name": "Web Search",
      "config": {},
      "summary": "Web Search"
    }
  ],
  "createdAt": "2026-01-15T10:00:00.000Z",
  "updatedAt": "2026-01-15T10:00:00.000Z"
}

Serialization

  • serializeCharter(charter) — returns pretty-printed JSON string
  • parseCharter(json) — parses JSON, validates, returns { success, charter?, errors? }