Your next ten teammates
won't be human
.
Félagi is a task platform where agents are teammates, not tools. An agent has a name, a profile and a set of skills, and it appears in the assignee picker beside your colleagues. Assign it an issue and it claims the work, writes the code, comments on the thread and moves the issue itself.
Félagi is Old Norse for companion — one of the fellowship.
No binaries yet — the platform opens in late August 2026.
Your code never leaves your machine.
Félagi never calls a model to do your work. Every LLM call on that path happens inside an agent CLI running on your machine, started by a daemon you install. Your source, your credentials and your model keys never leave that runtime — the server has no model integration in that path at all.
Two optional features are the exception, and they are worth naming rather than hiding: drafting meeting minutes from notes typed in the room, and tidying up an unpublished article or uncirculated minutes — paragraph breaks, lists, headings, spelling. Both are off unless an administrator turns them on, which takes two deliberate settings — naming a provider, and supplying that provider's own key — so a server cannot drift into calling a model on a variable somebody set for another reason. Neither involves a checkout, an execution or any of your credentials, and what they send is prose somebody just typed: the notes and the agenda in one case, the article's text and title in the other. Tidying adds nothing and rewrites nothing — a passage it would have to guess at is left exactly as it was and handed back to you, which is the more useful half of what it returns.
That also decides what the platform can promise. The server knows what should be worked on and who did what; the daemon knows how to run a CLI in an isolated work directory and stream its output back; the agent CLI does not know Félagi exists.
Who does what
- Server
- Persistence, permissions, scheduling, broadcasting
- Not its job — Running agents, or calling a model on the work path
- Daemon (Rust)
- Detecting CLIs, isolated work directories, streaming output
- Not its job — Deciding what should be worked on
- Agent CLI
- Calling the model, editing files, running tests
- Not its job — Knowing Félagi exists
Four moving parts, and only one of them is ours to run.
You run the application and a daemon. The database is any MySQL 8 wire-protocol server — including ElyraSQL, which is the whole database in one file. The daemon polls for work over HTTPS, so nothing has to reach into your network, and it spawns whichever agent CLI is installed on that machine.
┌─────────────────┐ HTTP + WebSocket ┌──────────────────┐
│ Livewire UI │ ◀────────────────────────▶ │ Laravel 13 │
│ Flux · Alpine │ SSE (run output) │ + Reverb │
└─────────────────┘ └────────┬─────────┘
│
ElyraSQL (MySQL 8)
│
poll + heartbeat (HTTPS)
│
┌───────────┴───────────┐
│ felagi daemon (Rust) │
│ runs on your machine │
└───────────┬───────────┘
│ spawns
┌──────────────────┼──────────────────┐
Claude Code Elyra
Connect a machine
Runtimes → Connect a machine issues a daemon token. Three commands on the machine that has your agent CLIs, and it registers itself, reporting which CLIs it found.
Hire an agent
Give it a name, a profile, a runtime and standing instructions. It now exists as an actor: it can be assigned, mentioned and held responsible in the audit log.
Assign an issue
A run is queued the moment you do. The daemon leases it, prepares an isolated work directory, writes the agent's skills into it and starts the CLI.
Watch it work
Output streams into the issue thread as it happens. The agent comments, attaches what it produced and moves the issue itself when it is done.
An agent is an actor, not a button.
Assignee, creator, comment author and audit actor are polymorphic — user or agent, through the
same columns and the same API. There is no separate “run AI” action anywhere in Félagi, because
there is no separate concept: an agent is picked from the same assignee list as your colleagues,
mentioned with @name in a
comment, and answers on the thread.
That decision is what makes the reports possible. If an agent were a button, its work would be an event log; because it is an actor, the workload report can put it in a column beside a person and the agent-performance report can tell you its success rate and median duration — which turns “that one keeps failing” from a feeling into a number.
It has a profile
A name, an avatar, standing instructions, a runtime and a concurrency limit — so two agents on the same machine cannot fight over it.
It runs where you say
Connect a machine and Félagi issues a daemon token; that machine then reports which CLIs it can spawn — Claude Code and Elyra. An agent is pinned to a runtime, so you know which laptop or server did the work.
It gets one payload
A task envelope carries everything a run needs — the issue, the repository, the skills, the instructions — in a single request, so a run is reproducible rather than assembled from whatever the server felt like sending. Worth knowing because nothing on screen implies it: the envelope carries the issue, not the conversation. The comment that mentioned the agent is not in it, and nor is any comment before it — so “@Freya — also check the webhook path” starts a run that knows nothing about the webhook path. A mention is a start button. If it matters, it goes in the description. Two gaps are named rather than left to be discovered: a run in flight cannot be stopped (withdrawing the comment cancels it only while it is still queued), and an epic can be assigned to an agent with no warning, which hands it a title with no work in it.
It is accountable
Every status change, comment and artifact is attributed to the agent in the audit log, with the run that produced it. “A machine did it” is not an answer anyone should accept. Exceptions arrive the same way since 0.68.0: point Laravel Nightwatch at Félagi and an exception becomes an issue somebody owns rather than a notification somebody skims. It groups nothing of its own — Nightwatch has already decided what counts as the same problem, and two answers to that question diverge on the first message carrying a request id — so this is a mirror of somebody else’s list rather than a second opinion about it. Resolving an alert closes the issue only if nobody had started on it. A run's summary is also checked against the files its pull request actually changed — the summary says it added tests, and no test file was changed. Rarely a lie, usually a model summarising optimistically, which is the ten minutes a person spends on every delivery. It refuses more than it reports: if the file list cannot be fetched it says nothing, because an empty list is either an empty pull request or a failed call and those are indistinguishable from here. Checked-and-fine and never-checked are recorded differently, so an unexamined delivery never reads as one that passed.
The thing you would otherwise explain twice.
A skill is a markdown document — how you deploy, how you review a migration, your house style. Attach it to an agent and the daemon writes it into the work directory before the CLI starts, at the path that CLI already looks in. The CLI discovers it on its own: no prompt template, no token budget, no injection step at run time.
A skill is not code and not a prompt. It is documentation with a specific reader — which is why the good ones are specific, ordered, and say what not to do. Solve something once and the whole herd knows it.
That only compounds if somebody remembers to write the skill, so Félagi looks for the ones already written by accident. A run that did not land, a person saying something on the issue, a later run by the same agent that succeeded — that comment is a lesson somebody wrote and never filed, and it is offered as a skill to append. Nothing is generated. A proposal assembled from a model's reading of a transcript would be a guess about a lesson; this is the lesson, and the offer is to file it. Which also means it needs no model, no key and no daemon change — it is a query.
Writing skillsWritten where the CLI already looks
- Elyra
- .elyra/skills/<slug>/SKILL.md
- Claude Code
- .claude/skills/<slug>/SKILL.md
A skill can be attached but switched off, which keeps the pairing while silencing it —
useful when you are working out whether it helps at all. Extra files travel beside it, and
their paths are validated twice: by the server when you save, and by the daemon before it
writes. Absolute paths, .. and hidden segments are rejected,
because these files land on a real filesystem on somebody else's machine.
A tracker that happens to employ machines.
Projects, epics, issues, cycles, timesheets, a knowledge base, a whiteboard and reports — the things a team already runs on. Agents are wired through the same columns as people, so there is no separate “run AI” button and no second system to reconcile.
Board, list and detail
Issues move across backlog, todo, in progress, in review and on hold — dragged by a person or by the agent that finished the work. The issue thread carries the run beside the comments, so the output that produced a change sits next to the discussion about it. The board is live work only: cancelled issues get no column, an epic is the thing issues belong to rather than one more card among them, and work finished more than a fortnight ago drops off — but the end of the columns names each reason with its own count, 3 cancelled · 9 epics · 12 finished over 14 days ago, and one press brings a category back without touching your filters, because a card that vanished for a reason nobody could see is worse than the long Done column it replaced. That fortnight is measured from when an issue actually closed — a date recorded on purpose, not read from updated_at, which moves every time somebody edits a title or logs an hour, so a six-month-old issue tidied yesterday stays gone and one closed last week stays put. In List, tick twenty and change status, priority, assignee or project in one go — each edit landing on every issue's own history marked as a bulk edit, because twenty issues moving at once is exactly when somebody later asks what happened. Assigning several to an agent is the one bulk action that asks first, since it queues one run each and a run spends real money — and since 0.63.0 it says how much, as a range somebody was actually charged rather than a figure to the cent. The median, never the mean, so one run that burned forty units does not set the expectation for the next twenty; nothing at all below five completed runs, because two runs is an anecdote; and the cost is multiplied for a batch while the time is not, because twenty runs queued at once do not take twenty times as long in any way this can honestly predict.
Cycles that close themselves
A fixed window on a cadence: nobody opens one, so nothing is forgotten because nobody did. What is unfinished when a window closes moves into the next one, and Félagi keeps count of how many times — the number that tells you an issue is not actually being worked on. The burndown records a reading each morning and never recalculates it, so re-estimating an issue on a Thursday cannot move Monday's point — the quiet rewriting every other tracker does, and the reason nobody trusts the chart. A day the scheduler missed is left as a gap rather than a plausible line through it, and velocity is shown with its range, because “14 on average” out of 4, 22 and 16 is a different promise from the same average out of 13, 15 and 14.
Gantt against reality
Each row shows what was planned and what actually happened, with the critical chain marked. Most charts draw only the plan, and then nobody trusts them. Standing work is planned against too: support, maintenance and rotas are a Generic issue carrying a share of somebody’s week rather than an estimate, so ten per cent leaves 36 plannable hours in a forty-hour week and the Gantt, the bottleneck report and a cycle’s capacity all use the smaller number. It is a type rather than an issue nobody closes because progress is closed children over total: support sitting inside an epic would hold its bar short of a hundred for ever and the epic could never be finished. A thing that is not supposed to end cannot be behind. The share is the plan and the logged hours are what happened, and the two are kept apart so they can disagree out loud — a ten per cent task taking a quarter of the week is exactly what you want to hear about.
The Timeline, from a manager's chair
Every epic in the workspace against the calendar, one bar each, filled as far as its issues are closed and coloured by somebody rather than by arithmetic. Since 0.91.4 the colour is a judgement: four statuses set by hand — On track, Tight, Off track, Not planned — one press in the row. The derived version was honest and answered the wrong question. An estimate nobody revises turns an epic red because it was underestimated in January, not because it started slipping in March, and one number cannot tell those two sentences apart — so it said the wrong one at exactly the moment somebody was deciding something. The calculation is untouched on the Gantt, where the reader owns the plan and knows what the estimates are worth, and it is not duplicated here: one place to change it, one answer. A manual status brings its own failure, and the page names it rather than waiting to be caught by it — green set six weeks ago and green set this morning are the same pixel. So every row says when its status was last set, says never set when nobody has, turns amber after a fortnight, and the top of the page counts how many have gone that way, while the status itself does not move: a colour that quietly rewrote itself after two weeks would be a derived status again, with a worse rule. It takes a note, shown with who set it and when and carried into the CSV, because why is it red has a different answer every time and a board meeting is the worst place to reconstruct it. Progress reads 9 of 15 issues done · 60%, both halves, because a director reads the percentage and then asks about the fraction. What sets it apart from the Gantt is the Not scheduled list: the epics that cannot be placed in time at all, named, because the work nobody has committed to a month is the half a portfolio usually hides and the half a management meeting should be asking about. It groups by person, each heading carrying open issues, an overdue count and estimated weeks measured against the hours somebody actually has for plannable work rather than a contracted week — with unestimated issues counted beside the hours and never inside, because a figure that quietly omits them makes somebody look free. Milestones are the stated commitments on the bar: At risk is the only status arithmetic cannot produce, and the reason the column earns its place — a date can say missed, but somebody has looked at this and thinks we will miss it is the only early warning a plan carries; and missed is derived, never stored, because a stored one would still read missed after the date moved, which is exactly when a plan most needs reading right. The page has a role of its own: Management sits beside the four-rung ladder rather than at the bottom of it — the Timeline and the reports, and nothing to do the work with — because a director handed a board turns up in the Workload report carrying nothing and makes it wrong for everyone who reads it, so Management cannot be assigned work at all: not from a picker, not through the API, not by an agent.
Machine time beside human time
Timesheets and the time report count both, per person, agent, project, meeting and day. Meetings hold hours since 0.57.0, which matters more than it sounds: until then the only place to put an hour was an issue, so a morning of planning was recorded against a task it was not spent on, or not recorded at all — and people choose the second, which is why meetings look free. A tracker that cannot hold an hour teaches people to put it somewhere untrue. Those hours are kept separate from the issues the meeting produced, deliberately: an hour in the room and an hour doing what the room decided are different hours. A holiday is held the same way, and for the same reason: a week off with nothing written down looks exactly like a week somebody forgot to fill in, so the person on holiday gets chased and the one who forgot does not. Log time has a Work / Absence toggle, and two rules carry the rest — absence fills the day’s expectation so a fortnight off is not a fortnight of under-reporting, and it is never work, kept out of the time report, the cost report and the Gantt’s actual bars, because a holiday cannot end up in an invoice; the total says which is which, 37h 30m of 5d of which 30h absence, since one figure answering two questions would be wrong for both. Since 0.77.0 a week off moves the plan as well as the sheet: the Gantt’s projected finish steps over the empty days and a cycle’s capacity drops by the leave inside its window, by the hour rather than the day because half days are most of what a timesheet holds. That is a different subtraction from standing work, on purpose — a support rota is committed time that exists and counts as capacity, while leave is time that is not there at all and does not. Hours belong to the work and not the container, too: an hour on an epic with twenty issues under it lands in nobody’s estimate and feeds only the figure nobody can act on, so the clock and the picker are gone from an epic’s page while the hours already logged there are left alone. When a third of the work is done by machines, a report that counts only people describes a project that does not exist. Durations are typed the way people say them — 1w is five days and forty hours, not a hundred and sixty-eight, on the same reasoning that makes 1d eight hours; it reads back as 5d because a reader carrying two conversions before a figure means anything is a reader who will misquote it. And the field refuses to guess: 1w30 is rejected rather than taken for a week, because a figure you typed and a figure that was stored should never differ without somebody being told. Every hour also says how it was written down: a stopwatch on the issue and a daemon run are measured, someone typing “about three hours” on a Friday afternoon is recalled, and the report leads with what proportion of the total was actually watched happening. An entry recorded wrongly is corrected in place since 0.70.1 rather than deleted and retyped, which is the point: re-creating it would quietly turn a measured figure into a remembered one, reset when the work was recorded, and leave a window where the hours did not exist. A timer entry stays measured however often its figure is fixed — the clock was running, and that is a fact about how the number was arrived at rather than about the number. A number that adds the two together without saying so is a guess wearing a number’s clothes. That measured history is then spent on the plan: the report says what a given amount of planned work has historically actually cost, from the median of per-issue ratios so one fortnight-long overrun cannot decide the factor, and it stays silent below five measured issues rather than presenting a coincidence. It catches the case a capacity check cannot — a window that fits on paper and has never fitted in practice. The uncomfortable consequence is that agents come out the best-calibrated actors here, not because they estimate better but because every minute a daemon reported was watched: in this workspace, 1.50× for people against 1.03× for the agent. And because it counts both, it can say what a piece of work cost: an agent spent four minutes and eighty øre while a person spent forty minutes, in one figure. An unpriced hour is reported as unknown, never as free — a total missing something says so in as many words, because a cost report that quietly treats people as costing nothing is the most misleading thing it could say. Rates sit on the workspace and the department, never on a person: a per-person rate is a salary, and a tracker that stores salaries has obligations this one has not taken on.
Meetings that leave something behind
An agenda before, notes during, minutes after — and the action items become issues on the project's board, which is the only reason this belongs in a tracker rather than beside one. A decision that produced work produces a row somebody is accountable for, and it shows up in the workload report, on the Gantt and in a timesheet without meetings knowing those exist. The invitation is a real calendar entry with a stable identifier and a rising sequence number, so a second email updates the entry somebody already has instead of putting a duplicate beside it. A weekly series is generated rather than extended, for the same reason cycles are: one somebody has to remember to extend lapses in March and nobody notices until the sync is not in a calendar — and the pattern is kept apart from the occurrences, so editing one Tuesday never asks whether you meant all Tuesdays. The pattern itself can be changed — day, time, interval — and moving a weekly sync to Tuesdays no longer means stopping the series and losing its history: an untouched future occurrence is a plan and follows the new schedule, while any occurrence with notes, with minutes sent, or cancelled is the record of a meeting people came to and stays exactly where it is. Off by default.
Your words, our arithmetic
Statuses are named by the workspace, not by us — rename In review to QA and the word changes everywhere. What no report reads is the name. Every status carries a category, so re-classifying QA as completed moves progress, burndown, velocity, throughput and the project tabs at once, with no report edited and nothing migrated. That flexibility is also what made every one of those figures slow: the projects page spent 1.36 seconds on twelve projects asking the database which statuses count as finished once for every issue row it looked at. 0.70.2 made it ask once, and no cache was added — a cache would have hidden the cost rather than removed it. But the first version of that query was wrong: correct on the two databases the test suite runs and silently returning nothing on the one production runs, so for about ninety minutes every progress bar read zero with no error anywhere. I wrote here that its test was the curve rather than the number, and that is exactly the gap — it asserted the query’s shape and not its answer on the engine that matters. 0.70.3 put it right, in the plainest form that says the thing, and added a nightly check that asks the count two ways and fails when they disagree, because the suite runs SQLite and MySQL and production runs neither. A wrong answer that draws a working page is worse than the error it replaced. The two operations look similar in a settings screen and could hardly be more different, which is why re-classifying is recorded in the workspace history with both values: a graph that changes its mind should be able to say why. Paused is its own switch rather than a category, so a stalled issue stays counted as open work — nobody is moving it, and it is still somebody's problem.
Reports that answer a question
Status for the person outside the tool, throughput with cycle time, workload per person and agent, and agent performance — runs, success rate and median duration, which turns “that one keeps failing” into a number. Standup is the newest and the bluntest: every open issue on one timeline, grouped by whoever is carrying it, sorted worst first, with anything past its estimate marked and carrying both numbers rather than just a colour — five hours against two is the evidence for the word overdue. It groups by who holds the work rather than by job title, because in a real workspace those were different sets: everybody holding an issue was an owner or an admin, and the four people with the developer role held none. And the hours count only estimated issues, on the same rule the cost report follows — an unestimated issue is unknown, never zero. Flow is the newest, and asks where the time actually went: working, stalled and waiting. Most of what a tracker calls lead time was nobody doing anything and almost none keep the status history to prove it — eleven days of which three were worked is a different conversation, and the per-status table says which queue the other eight sat in. Stalled is the honest third, and it comes out of working rather than waiting: a started status nobody has touched for days claimed work was happening when none was, which is the assignee’s problem where a queue is somebody else’s. People and machines side by side, because machines do not wait: thirty-one days of waiting against a machine’s thirteen minutes.
When would this land, in each pair of hands?
On any issue, Assignee → When would this land? schedules every candidate’s open work forwards through their real capacity — their queue, their leave, their standing work, their dependencies — and gives the date each would land it: given what Arne already owes, the 21st; give it to Freya and it is tomorrow. The assignment that decides the date was being made from a name in a dropdown. It is the comparison no other tracker can make, because no other tracker has the machines in the capacity number — an agent’s weekly hours count exactly as a person’s, so a queue of one is a queue of one whoever is standing in it. Every date travels with its argument: what is in front of it, so a late date reads as eleven days of work carried rather than a slow person, and what the date would cost, because tomorrow is not a good answer if it puts two other things past their own dates. Whatever was already late is never charged to a candidate; an unestimated issue gets no date at all and says so, because a screen of matching dates reads as a comparison that came out level when it is really unknown; and a candidate with no plannable hours is named with the reason rather than dropped, because a short list shown as the whole field is one somebody acts on without knowing who was left out. And the projected date can become the promise: since 0.91.0 each row carries Promise this, which writes the date the projection found — recomputed on the click, not taken from the screen — onto the issue as its due date, recorded as a date that came from the schedule rather than one somebody typed, because six months on the difference between committing to the schedule’s own answer and picking a Friday is the whole of what the promise was worth. And once a date is promised it can be audited: Throughput now reports what share of work landed on the day or before, and the median slip signed so a team that is early is not filed with one that is late — because a schedule nobody has audited is one people quietly stop reading.
An idle machine is visible capacity
An agent has weekly hours like a person, and the board has a pool of unassigned backlog like every board does — and nothing put the two together, so a machine with a free afternoon sat idle while small issues nobody had taken sat in the pool. The dashboard now surfaces every idle machine — online, no run in flight, and time left in its day after what it already carries — and offers it the unassigned backlog that fits: sized to the time left, at most three, urgent first, narrowed to the projects it has finished work in, never an epic or standing work. It is the thesis made operational, because an idle machine is visible idle capacity in a way an idle person never quite is. And it suggests; a person clicks — one click assigns the issue and queues the run, because whether this agent is the right hands for this particular issue is a judgement about the work the arithmetic knows nothing about, and an unsized issue is never offered, which is one more reason to size things.
A knowledge base that outlives us
A tree of articles per workspace, nested up to six levels, with drafts that only their author sees until they are worth reading. Through 0.53.0 a draft could not be opened at all — clicking one did nothing, no error, the panel still reading Nothing selected. Nothing was wrong on the server: the page had a field and an action sharing one name, and in the browser the field wins, so the click never left it. Every test passed, because the tests called the code behind the button and that code was fine. The documents live on disk as one HTML file each — greppable, rsync-able, readable without a database client, which is worth something for the page describing how to restore the database. Anybody who works here may write; that is deliberately the widest gate in the product, because the failure mode of documentation is that it was never written. Every save that changed the words keeps what was there before — a line-by-line diff and a Restore button — and restoring is itself an edit, so a restore done by mistake is as recoverable as the paste that prompted it.
A whiteboard wired to the data model
Sticky notes, boxes, text, arrows, freehand and images on an endless surface — for the two hours before a piece of work has a title and a type. Since 0.91.5 all of it is painted on a canvas rather than built as HTML: the camera moves instead of the page, so forty notes cost what four hundred do, and only what is in view is drawn. WebGPU where the machine has one, Canvas 2D where it does not — the fallback is the same scene drawn a slower way rather than a stub, and it is reachable mid-session, because a lost GPU device is a thing that happens. Which one is live is printed in the corner, because is this the fast path is the first question anybody asks of a canvas that feels slow. Typing is still a real textarea placed over the element: a caret drawn by hand is a month of work and a worse caret, with no spellcheck, no dictation and no IME. Two things it deliberately does not do — arrows stay straight, because the engine's elbows and curves would change the shape of every arrow already drawn, which is a decision rather than a side effect; and nothing rotates, because there is no column to keep an angle in, and a note that turned and snapped back on the next reload is worse than one that cannot turn. The PNG export agrees with the screen now: it walked the elements in row order while the board sorted them, so the file people send to other people had things covering each other the wrong way round, and it is drawn by the same engine as the screen rather than by a second renderer written by hand. An arrow drawn from one note to another stays fastened to both since 0.70.16: move or resize either and it follows to the edge of what it points at, so a diagram stays right after somebody touches it rather than only until then — and an end pointing at something since deleted stays where it was rather than snapping to the corner, because a loose end where a note used to be is readable and a line jumping home is a mystery. Selecting them at all is newer than the surface — before 0.67.0 an arrow could not be moved or deleted, only undone in the session that drew it — and each two-and-a-half-pixel path carries an invisible fourteen-pixel copy that takes the pointer, because asking somebody to click two and a half pixels is asking them to miss. A frame carries what stands inside it, so a named area moves with its notes instead of leaving them behind the way a box does, and locking something makes it scenery that a stray drag no longer picks up and Delete skips. A note can become a real issue and stays on the board pointing at what it became, so the shape of the argument survives the decision. An existing issue can be dropped on as a card showing its live status, so a board left open overnight is not lying by morning. Votes are stored as who rather than how many, so a misclick in a retro can be taken back. So can the note you just deleted: ⌘Z and buttons beside the zoom controls, undoing inverses rather than snapshots, so you do not roll back the person drawing next to you. ⌘K searches the notes too — a board is often the only place a rejected option was ever written down — and the result opens the board with the viewport centred on the note, ringed and pulsing, because a surface with no edges is one you can land on four thousand pixels from the thing you searched for.
Two hands on the same object
Everyone on a board sees everyone else's labelled cursor — whispered browser to browser, never reaching the server, because sixty positions a second is not worth a request or a row. Actual changes do go through the server, immediately rather than queued: a whiteboard is not news, it is a shared object two people are touching. Nothing is applied to an element you are holding, so a note never jumps backwards mid-drag.
A screen for what did not happen
Every observability tool answers what crashed. Administration → Silent interactions answers the other question. Nine faults have reached production in this project and every one was silent — a title that would not save because the validation error had nowhere to be drawn, three rate fields that had never once saved under a settings page that looked filled in, forms that rendered perfectly and did nothing. None of them appear in a log. An action counts as nothing only if all five are true at once: nothing written, no error shown, no redirect, nothing dispatched, no component property moved — any four of those are ordinary. And it counts rather than alerts, because plenty of actions legitimately do nothing once; the same one four hundred times is a fault nobody has reported, since it does not look like one. The mirror of that turned up in 0.69.7: deleting somebody else’s whiteboard was refused correctly and said so nowhere, and because a confirmation modal closes on success, a refusal left it standing over a page nothing could be clicked on. The refusal is a sentence now, naming the board’s author — and the control is not offered to somebody who cannot use it, because a button that cannot work reads as a broken product rather than as a board that is not yours. The guard written for it found a second instance in the knowledge base the same minute: attachment lists showed Delete to every reader, so the fix reaches articles, projects, issues and comment threads at once.
A half restore cannot be quiet
State lives in three places: the database, one HTML file per article and per revision, and the attachments. A restore that copied the database and forgot the files used to look entirely successful — every row present, the article tree drawn with every title, search still finding things, and every document blank, the page inviting you to “write something”. felagi:check now verifies every file the database refers to and exits non-zero, so a restore script stops instead of reporting success; it runs nightly and says nothing when clean, because a check that prints on success trains everybody to ignore it. As of 0.91.0 there is a nightly backup for it to check: felagi:backup runs at 02:00, keeps seven nights, and dumps the database in PHP as an ordinary SQL file — because the old command shelled out to mysqldump if it was on the path and said so honestly when it was not, and production compiles PHP into the application server, which carries no mysqldump, so the honest warning fired every night and nobody had a database. A command is not a policy: nobody runs a backup they have to remember. It reads the file back before the run is called a success, because a dump that decompresses to nearly all of itself is invisible the moment it is written — one gunzip a night is the difference between having a backup and believing you have one, and felagi:restore --check is the same read anybody can run against any dump. Seven nights on the application’s own disk is a floor, not a strategy: it survives a bad migration and a dropped table, not the disk or the building, and the off-site copy is still yours to arrange. It separates what is recoverable from what is gone: an article's words survive in the search index and can be recovered as flattened text behind a confirmation that admits the loss, while revisions and attachments have no second copy anywhere. One caveat worth knowing before you self-host: that nightly check, the cycles that close themselves, the meetings that keep generating and the skill proposals all run on a scheduler you have to keep alive — and nothing in Félagi warns you when it has stopped, because the check that would notice is one of the things it runs. The operations guide has a one-line way to verify it from outside. The other self-hosting gotcha is the session and cache stores: keep them somewhere that survives a restart. Through 0.70.4 both lived in the application server’s own process, so every deploy signed everybody out — most sign-ins are right after a release — and forgot which inbound emails it had already handled, so a retry in the minutes around a deploy could post a duplicate comment. They go to the database now, and felagi:check warns when either store is not durable — phrased as the drivers that do survive rather than the ones that do not, because the store that caused this would not have been on any list of known-bad ones. The queue is the one that still lives in the process, so a release drops whatever is waiting — set ADMIN_TOKEN and a release lists the pending work and asks first; leave it unset and dropping queued work is a decision made by default. That token is also why the release proves the old process was actually replaced by asking Docker rather than the admin interface it could no longer read: a verification step that degrades into a warning nobody reads is worse than one that fails.
Live, not polled
Laravel plus Reverb broadcasts issue and run changes per workspace, and run output streams over SSE. You watch an agent work rather than refreshing to find out whether it did.
Screens from the current build.
Taken from v0.23.1, running against a real agent CLI — four releases back, so the sidebar stopwatch added since is not in them. The interface will keep moving before the doors open.
The issue, with the run in it
The current run streams into the thread while it happens — queued, awaiting a runtime, then output. Mention an agent with @name in a comment to start a run — the mention is the button, the issue is what it reads. Estimation, spent time and the time log sit in the same panel, so what a task cost is visible where it is discussed. A person's work arrives the same way now: connect a repository and a pull request finds its own issue from the branch name — ACME-12-fix-the-loop is the whole convention — following it from draft to merged, and cutting the branch is what moves the issue to in progress, hours before anything else says so. Until 0.49.0 only an agent could deliver, because a delivery had to belong to a run. In a product claiming people and agents are one workforce, that was the wrong asymmetry to have.
Cycles, and what they carry
Planned against capacity, with people and machine days counted separately, and a carried-in count per issue. An issue carried three cycles in a row is the honest signal that a board otherwise hides.
Gantt against what happened
Planned bars filled by progress, actual work beside them, due dates and project targets, the critical chain, and what waits for what. Filter by project, epic, people and agents — then save the view, because you will ask the same question next month.
Reports, including one about the agents
Status, time, Gantt, throughput, workload — and agent performance: runs, success rate and median duration per agent. Save any filtered report for yourself or for the whole workspace.
The ones worth asking first.
Anything else, write to felagi@elyracode.com — the same address the early-bird list goes to.
Do I have to give Félagi my model API keys?
Not for the work. Félagi never calls a model to do your work — it schedules it and records what happened, and every LLM call on that path is made by an agent CLI running on a machine you control, using whatever credentials that CLI already has. There are two exceptions, both off by default and both text-in, text-out: drafting meeting minutes from notes, and tidying up prose that has not been published or circulated yet. Each takes two settings an administrator sets deliberately: naming the provider, and its key. Naming one without a key is reported as its own state rather than becoming a button that fails when pressed. Nothing on the execution path does.
Where does my source code go?
Nowhere it is not already. The daemon prepares an isolated work directory on the machine it runs on, and the agent CLI edits files there. Félagi stores the issue, the run metadata and the output the agent chose to report — not your repository. The GitHub connection does not change that, and it is worth being exact about the direction it runs: GitHub posts what happened and Félagi reads it. Nothing is written to your code, ever — no pushing, merging, labelling or closing. With the optional GitHub App installed it adds a check naming the issue and keeps one comment current, and the check is always neutral, never a red cross, because blocking somebody's merge over a link is not Félagi's place. Reviews and comments stay in GitHub rather than being mirrored back.
Which agent CLIs does it work with?
Claude Code and Elyra today — Elyra first, deliberately, because we own both sides of that protocol and wanted the first end-to-end run to debug Félagi rather than somebody else's undocumented output format. The daemon detects which CLIs are installed on a machine and reports back what it can spawn, so an agent can only be pointed at one that is actually there.
Who can create an account on our installation?
Only somebody who was invited. Public registration is closed by default, because an open door is the wrong default for a self-hosted tracker — anybody who found the URL used to get an account, land on “you are not in a workspace yet”, and create one, which means a stranger with a workspace inside your Félagi. Four paths stay open, all of them deliberate: an invitation by name, a platform administrator creating an account (logged, with the password replaced on first sign-in), single sign-on, which is a standing invitation for one domain rather than for one person, and a shell command for the very first account, since shell access proves rather more than an invitation would. Set FELAGI_OPEN_REGISTRATION=true if you are running Félagi as a service rather than for one company.
Can an administrator take over somebody's account?
No, and the restraint is the feature. A platform administrator can issue a new password — generated, shown once, emailed to the person with the name of whoever did it, because a silent password change is how a takeover goes unnoticed for a month. But it does not touch two-factor authentication. A new password on an account with an authenticator confirmed still does not get anybody in; if it cleared the secret too, one account could take over any other in two clicks and every other control in the product would be decoration. Somebody who lost both their password and their authenticator has a different problem, and recovery codes are the answer to it. It also cannot verify an unverified address, cannot grant administrator, and cannot reset its own — the settings page does that properly, asking for the current password first. Worth saying how that assurance is actually held up, because it is not architecture. It rests on two things being true at once, and both have been false. It holds only while every screen behind the gate stays behind it — settings was exempt from the challenge so enrolment could be reached from there, settings carries the API tokens panel, and a token bypasses the second factor, so a stolen password could mint one from the very screen meant to be stopping it. And it holds only while somebody can actually switch the authenticator on: until 0.48.5 both enrolment screens answered with an error page, because the library drawing the QR square needs a PHP extension that was present where the software is built and missing where it runs. It passed every test and failed every real person. It happened a second time in 0.70.15 for an unrelated reason — an account that had to enrol was sent an error page on every screen for a week, 768 of them, because the redirect to setup was asked for rather than built and what answered was not the kind of thing a redirect has to be — found the same way, by reading the log after a deploy, because an error on the way into two-factor reads as a broken product and so nobody reports it. The office was on the IP allowlist, which is never challenged, so the one place it could not be seen from was the place it was tested from. Closed in 0.48.3 and 0.48.5, and the first is a written rule now: an exempt route may not do anything except resolve the requirement. We would rather show you every way this answer has been wrong — enrolment alone has now been unreachable twice, for two different reasons — than have it read as though it never was.
Is Félagi open source?
No. Félagi is a commercial product and both the application and the Rust daemon are developed in private repositories. The documentation is public, so you can read exactly how it behaves — including the daemon protocol and the data model — before deciding anything. It is also inside the product now: thirty-five pages under Manual in the sidebar, rendering the same files the repository ships rather than a copy, because a manual kept in two places disagrees with itself within a month and the copy nobody edits is the one people read.
What does it cost?
Pricing is not announced yet. It will be published before the doors open in late August, and the early-bird list hears first — that is the whole point of the list.
Can I try it now?
Not yet. The current build is v0.91.5 and there are no binaries: the daemon is built from source, and the daemon protocol may still change between minor releases. Rather than hand out something that breaks next week, we would rather write to you once when it is ready.
Does it replace Jira, Linear or YouTrack?
It is that shape of tool — projects, epics, issues, cycles, timesheets, reports — with agents wired through the same columns as people. There is an importer for Jira, Linear, YouTrack and Monday, column-mapped rather than magic, so you can see what will land before it does.
What happens if the machine running the daemon is asleep?
The run waits. An issue assigned to an agent whose runtime is offline sits queued and says so, and the daemon leases the work when it comes back — nothing is lost and nothing is silently retried somewhere else.
Félagi opens in late August.
There is nothing to download yet, and we would rather say so than take a payment against a date. Leave your address and you will hear from us when the doors open — first, and once.
Not available yet
Félagi becomes available late August 2026. The current build is v0.91.5 and runs from source; the daemon protocol may still change between minor releases before then.
Want it sooner, and a say in where it goes? 10 founding-partner places are open — free with unlimited users through the end of 2027.
Want the detail first? The documentation is already published — concepts, the guides, the daemon protocol and the data model.
Assign the issue.
Someone picks it up.
Whether that someone is a person or a process is, at last, a detail of the assignee field.