Projects
A project is what you set up and look after in Tares: one job, with everything it needs on one page. It uses:
- sources: the data its triggers read,
- triggers: what should fire, and on which sources,
- agents: the Tares agents those triggers wake, and the agents they hand off to,
- MCP servers: the external tools those agents may use,
- skills: the instructions its agents load when a task calls for them.
These parts belong to the instance, not to one project. Any number of projects can use the same source, trigger, agent, MCP server or skill: the same GitHub MCP server in two projects, or one agent serving two goals. What a project owns is its goal and its wiring:
- which trigger wakes which agent in this project, and whether that agent is on here,
- which agent takes over when another concludes with a verdict (its handoffs).
So one agent can be woken by trigger A in one project and by trigger B in another, on in the first and off in the second, with different handoffs in each. A run belongs to the project whose wiring started it: each project’s results, Activity, today’s totals and spend count its own runs only. When two projects wire the same trigger to the same agent, it runs once and shows in both.
Every part is also an ordinary Tares object on its own page; the project page brings together the ones it uses. The project that created a part is recorded as its maker (the part of project badge), which never stops another project from using it.
All resources in the sidebar lists every part on the instance, one table per kind, with the
projects that use each (GET /api/resources). Use it to see what is shared, or what no project
uses any more.
The Default project
Every instance has a Default project. It holds whatever was made outside another project: a
source, trigger, agent or MCP server created on the API or over MCP with no project lands there,
and so does a part that no project uses any more. When another project takes in a part from the
Default project, the part leaves Default, and Default’s wiring of it comes along, so what ran
there keeps running in the project it joined. The Default project is listed first under
Projects and has a page like any other project, without Edit or Delete: it cannot be
deleted.
In the console, a trigger or agent is added from a project’s Advanced setup (+ next to Triggers or Agents), so it goes into that project.
Starting a project
A project starts in one of three ways, all from Projects → Create new:
- Guided setup. Say what the project should achieve; Tares plans the sources, wake-ups and agents, reusing what is already on the instance, and shows the plan before anything runs. See Guided setup.
- From a Tares template. A template is a ready-made setup: pick one, answer a few questions, click Start, and Tares creates the objects behind it.
- From existing objects. Assemble a project by hand from sources, triggers, agents and MCP servers you already have. Nothing is created; the project uses them.
A guided setup you leave part way is kept as a draft: it shows on the Projects list with Finish setting up, which reopens it where it stopped, and Delete draft, which removes it. Nothing of a draft is set up yet, so deleting it touches no part.
The Projects entry sits at the top of the console sidebar because it is the quickest way from an empty instance to something running.
Tares templates
| Template | What it does | Guide |
|---|---|---|
| AI SRE demo (tagged demo) | Watches the bundled demo stack: three sources keyed by service (Prometheus metrics, the api-server’s logs, the alerts Prometheus fires), a trigger over all three that wakes an agent when an alert fires, and an agent that writes the first incident note back onto the timeline. The page has Cause an incident and Clear the fault buttons. | Build an AI SRE |
| Shared code context | Watches commits across your code repositories and keeps one shared context repository current: an agent reads each change and updates the pages that cover it, opening a pull request. | Keep shared code context current |
| Challenger workflow | Codex on your laptop challenges Claude Code’s plan and every commit in a marked session; Tares keeps the exchange on one timeline per session, writes the session summary when it ends and proposes what to remember for the next session in that repo. Created by itself the first time a session is marked with /tares:challenger. | Challenge Claude Code with a second model |
A template marked demo needs the demo stack rather than your own systems; its setup page shows what to run first, with copy buttons, and detects what is already running.
Starting from a template
Open Projects → Create new. That page asks for the goal of a guided setup; the template
gallery is behind Start from a template on it (/projects/new/templates). Pick a template
card, click Set up. The setup page asks only for what it cannot work out itself: the AI SRE demo looks at Docker and fills in the Prometheus and api-server
URLs from the running containers; the shared code context wizard lists the repositories your
GitHub token can see. The last step shows exactly what Start creates, with the names of the
objects. Some setup steps check themselves (a model provider that is
already set shows as done).
A template only takes over parts it plans that are still in the Default project, or new ones. If a part with a planned name was made by another project, the template stops and names that project, because a template reconfigures what it plans.
Assembling from existing objects
Next to the templates, From existing objects builds a project without creating anything: name it, pick the objects it is made of, click Start.
- Any part can be picked, whichever project uses it already. Parts in other projects stay there and are shared; parts that were only in the Default project leave it.
- The picker shows each source with what it watches (its repository, for example) and finds it by that too.
- What an object needs comes along: an agent brings its trigger and MCP servers, a trigger brings its agents and its sources.
- An agent that another project made joins with its trigger but off, and without that project’s handoffs: turn it on, and add handoffs, in this project.
A project assembled this way can also start empty and fill up as you add objects on its page.
- Edit changes the list. Removing a part takes it, and this project’s wiring of it, out of this project only. Nothing is deleted; a part no project uses any more goes to the Default project.
- Delete removes the project with its triggers, agents and MCP servers (see Deleting a project).
- There is no plan behind a hand-assembled project, so Repair and customized do not apply: edit the objects on their own pages, the project shows them as they are.
The project page
Opening a project shows its Overview: what the project is for and what its agents found. The parts that make it work (sources, triggers, runs, raw events) sit one click away in Advanced setup, for when something does not work as expected.
Overview
- The goal: one line saying what the project should achieve. A project made from a template starts with the template’s goal; click it to change it. A project without one asks for it (“What should this project achieve?”).
- One sentence of how it works, built from the setup, for example “When checkout-errors gets more than 5 events in 5 minutes for one service, checkout-triage looks first. If it concludes investigate, checkout-rca digs in.”
- A health line: Working (what is receiving and how many agents are ready), Needs attention (the most important issue first, with a button that opens the thing to fix, such as a source whose token was rejected), Paused, or Setting up (nothing has arrived yet).
- Today’s totals: how many things the agents looked at, how many need action, and what the project spent (days start at midnight UTC).
- What the agents found: one card per conclusion, newest first, with Load older to page back. A card shows the headline, a short summary and the next step, marked Needs action or No action. When one agent hands off to another, the card is the last agent’s conclusion, not each step. Findings recorded by outside agents show here too.
At the top right are Setup, Pause (or Resume) and the template’s actions (the AI SRE demo’s Cause an incident and Clear the fault).
Click a card to open the result: the next step first, then the full note, then How Tares got here (what arrived, which agent looked, what it concluded and which skills it used, in plain sentences), with the time and cost. Mark as handled records that someone dealt with it, and clears again with Mark as not handled. See the raw events and runs opens the same thread in Advanced setup’s Activity.
Setup
Setup shows the project as four steps, each item with its state and a Change link to its page in Advanced setup:
- Watches: the sources, each receiving, silent, waiting for its first event, paused or in error.
- Wakes when: each trigger’s condition in plain words, and how long it waits before waking again for the same entity.
- Agents: which agent looks first and which digs in after a handoff.
- Know-how: the project’s skills and which agents loaded them.
A template’s panels (the context repository, the demo stack) are shown here too, with Edit and Delete for the project. Advanced setup at the top right leads to everything else.
Advanced setup
Advanced setup has its own navigation on the left, with Back to the project at the top. The header has Pause (or Resume), Edit and Delete, and cards for the counts that matter (runs, trigger last fired, created, plus whatever the template adds). The navigation:
- Activity: everything that happened in the project, one thread per incident, with the firings, runs and raw findings (see Activity).
- Events: the latest events from the project’s sources.
- Sessions: shown when the template reports sessions (the challenger workflow).
- Sources, Triggers, Agents and Skills: one section each, listing the project’s objects by name, with a + to add one.
- Settings: MCP servers, Subscribers (the webhooks and Slack channels its triggers deliver to), Keys (project keys for agents outside Tares) and Change history.
Click a section name for an overview table of everything in it. Click one object to see just that object:
- a source: its status, labels, what in the project reads it, and its recent events. Adding one offers Add existing source, which takes a source from elsewhere into the project, and Connect a new source, which creates one in it. Remove from project takes a source out without deleting it.
- a trigger: its settings, what it wakes, and its recent firings.
- an agent: its settings, its handoffs in both directions (whom it hands off to, who hands off to it), and its runs. The Agents section also lists the outside agents that joined the project, marked external.
- a skill: its body, and which agents loaded it. See Skills.
Links made for the old tabbed page (?tab=...) and links to a setup view (?view=activity and
the others) still open Advanced setup.
Pause turns this project’s wiring off: its agents stop running here. A trigger stops only when no other running project uses it, so pausing one project leaves the others alone. Sources keep ingesting unless you tick “also pause its sources”, in which case Resume brings back exactly what Pause stopped. Edit reopens the setup with your values; on save the project re-plans and applies only what changed.
Activity
Activity lists what happened in the project, newest first, one row per thread. A thread starts with a firing (a trigger fired, and the runs it woke) or with a run that no firing woke (started by hand, or the first look when a project starts). Each row shows the time, what fired or who ran, the entity, and each agent that ran with how it ended.
Open a row to see, in place:
- the firing: its trigger, an excerpt of the payload it carried, and where it was delivered (Tares agents, Slack channels, webhooks), with a link to the full firing,
- each run: its finding, what it produced, the skills it loaded, and a link to the run,
- what each run led to: a rerun sits under the run it repeats, a handed-off run sits under the run that handed off, and a firing that a run’s finding tripped sits under that run, with the runs it woke in turn. A handoff skipped for its cooldown shows as a note on the run that would have handed off.
So a watcher’s alert, the finding it wrote and the root-cause run that finding started read as one thread, however many agents it took. Filter by trigger, agent, outcome (found something, nothing to report, failed, stopped by a cap, and so on) or entity, and use Load older to page back.
A finding an agent outside Tares recorded is a thread of its own, marked as recorded from outside Tares. Runs and firings from before this view existed show under the project of their agent or trigger; what woke them shows as unknown.
Outside agents
An agent that runs outside Tares (Claude Code, Codex, your own service) can work inside one project next to its Tares agents: be woken by the project’s triggers, read its sources, follow its skills, and record findings that land on the same timelines. It does this with a project key. The guide Connect an outside agent to a project walks through it with Claude Code.
Project keys
A project key reads one project and records findings in it, and nothing else. Make one on the project page under Settings → Keys: name it after who holds it (the findings it records carry that name), and copy the secret, which is shown once. Revoke there cuts the agent off.
With a project key:
- Reads cover the project only.
POST /readand stats read the project’s sources, plus the findings recorded in this project.GET /api/projectsand/catalogshow this project only. - The project’s skills, timeline, findings, results, outline and health are readable; another project’s are not.
- It records findings in the project, and manages its own subscriptions to it.
- Everything else answers 403
this key only reads project <name> and records findings in it.
Project keys are checked on a secured instance (one with TARES_AUTH_TOKEN, see
Deployment). On an open instance nothing is checked, so there
are no keys to hand out; the MCP tools then take the project by name.
Join: be woken by the project’s triggers
POST /api/projects/{id}/subscribe {"url": "https://my-agent.example.com/hook"}, or the MCP tool
join_project(url), sends every firing of every trigger in the project to that URL, including
triggers added later. The body is the usual firing body:
dispatch_id, trigger, project, kind, key (the entity), fired_at, payload (the
timeline), and run_ids, the Tares agent runs the same firing started. Subscribing the same URL
again with the same key returns the subscription it already has.
DELETE /api/projects/{id}/subscribe/{subscription_id} leaves; a project key removes only its own.
The project’s Agents section lists the outside agents that joined, marked external. Click one to see where it delivers (the URL masked), the key that made it, and the last delivery. Revoke there stops the deliveries; the key keeps working until you revoke it under Settings → Keys.
Record a finding
POST /api/projects/{id}/findings records what an outside agent concluded:
{ "entity": "checkout", "finding": "Errors began after deploy 9f2c81d ...", "verdict": "rca", "label": "service" }entityis what the finding is about,findingis markdown (up to 64 KB).verdictis optional, one lowercase word such asrcaorresolved.labelis the label the entity is a value of; left out, it is the entity label of the project’s first trigger.headline(one line, up to 100 characters) andnext_step(up to 300 characters, left out when nothing needs doing) are optional; they head the finding’s card on the Overview. Left out, both are taken from the note.
The finding is stored like a Tares agent’s: it lands on the entity’s timeline, every later read of
the entity includes it, and a trigger over the findings source can fire on it. It carries the
key’s name as its agent, shows as a card under What the agents found on the Overview, and in
Advanced setup’s Activity it is a thread marked as recorded from outside Tares. A key named like one of the instance’s Tares agents cannot record findings (409); give it
another name.
GET /api/projects/{id}/findings?entity=&agent=&limit= lists the project’s findings, newest
first, the Tares agents’ and the outside agents’ together; each carries external: true or
false. POST /api/projects/{id}/stats {"by": "service", "window": "30m"} counts events per value
of a label over the project’s sources, this window against the one before, with optional where,
top and sources.
Deleting a project
Delete removes the project and the triggers, agents and MCP servers it made, revokes its keys
and ends the subscriptions of the agents that joined it. A part it made that another project uses
is kept, and that project becomes its maker (listed as kept_shared); a part it only used is not
touched. Its skills go unless another project uses them. Sources are shared, so the dialog lists
the project’s sources with a checkbox each, none ticked:
- A ticked source is deleted too, unless another project uses it; then it is kept and listed as kept.
- An unticked source stays connected. If no other project uses it, it moves to the Default project.
- Events already stored stay unless you tick “also purge the events of the sources deleted and its triggers’ firings”.
The Default project has no Delete button.
Deleting a source on its own page lists the triggers and agents that depend on it and offers to delete them as well; without that the delete is refused, naming what would break.
Deleting any part on its own page takes it off every project’s list: deleting it was the choice, so no project shows it as missing. The agents a deleted trigger woke stay, with no trigger of their own: a handoff still starts them, and you can give them another trigger. A deleted MCP server drops off the agents that listed it.
Makers and templates
A part carries a part of project badge on its own page naming the project that made it, and is fully ordinary there. The maker never locks it: any project can use it. For a project started from a template:
- Edit a part by hand and it is marked customized; the project keeps your version on its next re-plan instead of overwriting it. Repair resets a customized part back to the plan.
- A part that already existed with the same name in the Default project is adopted, not duplicated (this is how the AI SRE demo picks up a demo catalog you imported earlier). One that another project made stops the template, naming that project.
Setting a trigger’s project (PUT /api/triggers/{name} with project) adds it, with its
sources, to that project; it stays in the projects that already use it, and leaves only the
Default project.
Upgrading from views
Earlier versions had views: saved sets of sources that triggers watched. Views are gone; a
trigger now names its own sources, filters and key_field. The first start after upgrading
does the move for you:
- Each trigger takes the sources, filters and key field of the view it used. A trigger whose view was already deleted is paused with no sources, so it shows up to be fixed or deleted.
- Everything is placed in a project. A trigger that was in no project goes to the Default project, an agent follows its trigger, an MCP server goes to the project whose agents use it (else the Default project), and a source in no project joins the Default project. Objects a template or custom project already held stay there.
- Schedule triggers keep their last tick, so none fires early.
The pass runs at every start and changes nothing the second time. A catalog file exported while
views existed still imports: each trigger that names a view gets that view’s sources, filters and
key field. /api/views, POST /query and POST /derive now answer 404; read with
POST /read instead.
In the catalog
A project can be declared in catalog YAML, so a seed catalog can ship one and tares up creates
it on first boot:
projects:
- template: ai_sre_demo
name: AI SRE demo
params:
prometheus_url: http://localhost:9090
api_server_url: http://localhost:8080
container: tares-demo-api-server
- template: custom
name: checkout watch
objects:
- { kind: source, name: checkout_logs }
- { kind: trigger, name: checkout_errors }
- { kind: agent, name: incident-first-look }Import creates the project by name or updates an existing one; export includes the section with
the parameters (never credential values). Triggers, agents and MCP servers name their project
(the one that made them), and sources the projects they are in, by name. Each project’s wiring
exports under a top-level wiring: section, its wake-ups as {project, wake, agent, enabled} and
its handoffs as {project, agent, verdict, to, cooldown}, and import restores it. A project’s goal is exported and imported with it. The Default
project is listed under projects: only when it has a goal, and then only its goal is imported;
objects that name it go to the importing instance’s own Default project, and so do
objects that name no project. The project’s skills export under a top-level skills: section (see
Skills). The old form (usecases: with recipe:) is still read
until two releases after 1.14.
The API
Everything the console does is on the management API. {id} is the project id (uc_...) from
GET /api/projects.
| Call | Does |
|---|---|
GET /api/projects/templates | the available templates with their parameters, tags, setup steps, actions and starter sentence |
GET /api/projects/templates/{key} | one template, hidden ones included |
POST /api/projects/templates/{key}/detect | what the template can work out from the environment (the AI SRE demo asks Docker what is running) |
POST /api/projects {template, name?, params, goal?} | create from a template; all or nothing. goal is one line of up to 200 characters; left out, the template’s goal |
POST /api/projects {template: "custom", name, objects} | assemble from existing objects; objects is [{kind, name}, ...], kinds source, trigger, agent, mcp_server. Any part on the instance can be named |
GET /api/projects and GET /api/projects/{id} | list (the Default project first, with default: true; drafts with status: "draft"), or one project with its objects. An object is missing only when it no longer exists |
GET /api/resources | every part on the instance (sources, triggers, agents, tools, skills, keys), each with what it is, its state and used_by, the projects that use it |
PUT /api/projects/{id} {params, name?, goal?} (or {objects} for a custom project) | re-plan and apply the difference. A body with only goal sets it (an empty string clears it) and changes nothing else; this works on the Default project too |
POST /api/projects/{id}/sources {name} | add an existing source to the project |
DELETE /api/projects/{id}/sources/{name} | take a source out of the project; refused while one of the project’s triggers reads it |
POST /api/projects/{id}/pause and /resume | triggers and agents off or on; {"sources": true} on pause also pauses its sources |
POST /api/projects/{id}/repair {key} | re-create or reset one planned object (template projects only) |
POST /api/projects/{id}/actions/{name} | run one of the project’s actions (inject with {scenario} and clear on the demo; summarize with {session} on the challenger workflow) |
GET /api/projects/{id}/summary | the project plus what its page shows (runs, sources, last fired) |
GET /api/projects/{id}/timeline | the Activity threads; see below |
GET /api/projects/{id}/outline | how the project works in plain sentences: sentence, and watches, wakes, agents, skills with each item’s state |
GET /api/projects/{id}/health | state (working, attention, paused, setting_up), a message, and issues, most important first, each with a severity, message and a suggested fix |
GET /api/projects/{id}/results?limit=&before= | what the agents found, newest first; see below |
GET /api/projects/{id}/results/{run_id} | one result with its full note and the steps that led to it |
POST /api/projects/{id}/results/{run_id}/handled {"handled": true} | mark a result handled, or false to clear the mark |
GET, POST, PUT, DELETE /api/projects/{id}/skills... | the project’s skills; see Skills |
GET /api/projects/{id}/keys and POST /api/projects/{id}/keys {name} | the project’s keys (no secrets), or make one; the secret is in the response only. See Project keys |
POST /api/projects/{id}/subscribe {url} and DELETE /api/projects/{id}/subscribe/{sid} | join the project with a webhook, or leave |
GET /api/projects/{id}/external-agents | the agents that joined: masked URL, key, last delivery |
GET /api/projects/{id}/findings and POST /api/projects/{id}/findings | list the project’s findings, or record one from outside |
POST /api/projects/{id}/stats {by, window?, where?, top?, sources?} | counts per label value over the project’s sources, returned as {stats: text} |
DELETE /api/projects/{id}?delete_sources=a,b&purge_events=true | delete the project with the triggers, agents and MCP servers it made (those another project uses are kept, listed under kept_shared) and the skills no other project uses. delete_sources names the sources to delete too (each one kept, and listed under kept, when another project uses it); all names every source of the project. Left out (or none), every source is kept. purge_events purges the deleted sources’ events and the deleted triggers’ firings. A draft is deleted the same way |
GET /api/catalog/dependents?kind=source&name=... | what stops working if that source goes, in delete order; DELETE /api/sources/{name}?cascade=true takes it along |
Creating an object in a project: POST /api/sources, /api/triggers, /api/agents/builtin and
/api/mcp-servers take project (an id or a name; the Default project when left out). The list
endpoints report it: project on a trigger, agent and MCP server (the project that made it), and
projects (every project it is in). An agent may use any trigger and MCP server on the instance.
The agent routes take ?project= for one project’s view of an agent: GET /api/agents/builtin
returns each agent’s trigger, handoffs, on or off and run counts as that project wires them, and
POST /api/agents/builtin/{name}/enable (or /disable) and GET .../runs act on that project
only. PUT /api/agents/builtin/{name} with a project other than the agent’s maker changes that
project’s wiring, not the agent’s own trigger and handoffs. The routes behind guided setup and
drafts are on Guided setup.
The pre-1.14 routes (/api/usecases*, with their old response shape) keep working until two
releases after 1.14.
The results API
GET /api/projects/{id}/results returns one result per chain of runs: when one agent hands off to
another, the result is the last agent’s conclusion. A run that concluded with nothing to report
is listed only when it wrote a note.
{
"results": [
{
"id": "run_4be1c0d2a9f3", "thread": "9f2c81d4…", "at": "2026-09-29T09:14:40+00:00",
"entity": "checkout", "kind": "action",
"headline": "Checkout returns 502 since the payments deploy",
"summary": "Errors began two minutes after deploy 9f2c81d of payments-api…",
"next_step": "Roll back payments-api to the previous release.",
"verdict": "rca", "chain": ["checkout-triage", "checkout-rca"],
"cost_usd": 0.04, "duration_ms": 24000, "handled": null, "external": false
}
],
"next_before": "2026-09-28T17:02:11+00:00",
"today": { "looked_at": 12, "found": 1, "spent_usd": 0.31 }
}kindisno_actionwhen there is no next step, or the verdict is one such asok,resolvedornoise; otherwiseaction.headlineandnext_stepcome from the agent’sconcludecall. For a run without them, Tares takes them from the note: its heading, bold lead or first sentence, and the text after “Next step:”, “Next:” or “Recommendation:”.todaycounts since midnight UTC: threads started, results that need action, and what the project’s runs cost.handledis{at, by}once someone marked the result handled.- Page with
beforeset to the previous page’snext_before.
The timeline API
GET /api/projects/{id}/timeline returns the Activity threads, newest first:
| Parameter | Does |
|---|---|
limit | threads per page, default 50, at most 200 |
before | an ISO timestamp; pass the previous page’s next_before to get the next page |
trigger, agent, entity | keep threads where that trigger fired, that agent ran, or that entity appears, anywhere in the thread |
outcome | keep threads with a run that ended this way: finding, no_op, failed, capped, empty, exhausted, running, or none for a firing that woke no agent |
{
"threads": [
{
"id": "9f2c81d4…",
"kind": "firing",
"at": "2026-09-29T09:14:03+00:00",
"trigger": "incident",
"entity": "api-server",
"fired": {
"dispatch_id": "9f2c81d4…", "trigger": "incident", "kind": "condition",
"key": "api-server", "fired_at": "2026-09-29T09:14:03+00:00",
"payload_excerpt": "…the first 600 characters of what the firing carried…",
"subscribers": 1
},
"deliveries": [
{ "kind": "tares", "target": "incident-first-look", "ok": true, "error": null,
"at": "2026-09-29T09:14:40+00:00" }
],
"runs": [
{ "id": "run_4be1…", "agent": "incident-first-look", "status": "ok",
"outcome": "finding", "woken_by": "trigger", "skills": ["checkout-triage"],
"finding": "…", "children": [], "firings": [] }
]
}
],
"next_before": "2026-09-29T08:02:11+00:00"
}A thread’s kind is firing or run (a run no firing woke; fired is then absent). A finding
recorded from outside Tares is a run thread whose run has external: true and
woken_by: external. A run carries
the fields of the agent’s runs list (status, outcome, verdict, entity key, model, rounds, tool calls,
cost, duration, finding cut to 4,000 characters, results, skills, woken_by), plus children (its
reruns and the runs it handed off to) and firings (firings its finding tripped, each shaped like a thread). next_before is
null on the last page.
Want a template that is not here yet? A template is a small Python class (parameters, the objects it plans, an optional summary and actions). Tell us what you would set up next: help@glassflow.ai.