Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Starf An activity monitor for Apple silicon that never invents a number e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Start here
Concepts
Release notes
What's new
Elyra

Knowledge base

A tree of articles, one per workspace. Knowledge, in the sidebar.


The shape

A tree on the left, a document in the middle, its headings on the right.

Nesting is arbitrary, up to six levels — unlike an epic, which is one level deliberately. A plan with grandchildren stops being readable; documentation is the opposite, because the nesting is the organisation. Six is deeper than any documentation that is working.

Only open branches are drawn. A collapsed section costs nothing, which is what keeps a base of several hundred pages usable.

Following a link to a page four levels down opens the branches above it. Otherwise you would see the article and not where it sits, which is most of what a tree is for.

Four states, and the two questions they answer

Every article is in one of four states, and the state is the only thing that decides both who may read it and whether it is part of the tree.

State Who may read it Where it appears
draft Its author, and nobody else Drafts. Never the shared tree
in_review Everybody in the workspace The tree, marked
published Everybody in the workspace The tree, unmarked. The ordinary state
archived Everybody in the workspace Search and links only. Kept, not deleted

A new article starts as a draft. Not really a permission — a courtesy. Half a page is worse than no page, and somebody who finds one through search will act on it.

Published carries no badge, anywhere. A tree where every row says "published" has taught the reader to skip badges, and the one row that needed a mark is the one they will then miss.

Archived is the state that was missing. Before 0.54.1 there was a single published boolean, so taking a page out of the way and marking it unfinished were the same act — and it hid the page from everybody. Now the old deploy process can stop being part of how the base is organised while staying readable to anyone who searches for it or follows a link somebody wrote down.

Why it is not a boolean

That flag answered three questions at once: how far the writing had got, who could read it, and whether the article was in the tree. Collapsing them lost work. An article published under somebody's draft was permitted to everyone and reachable by nobody — no branch led to it, and its own author could not find it in the tree either.

An article whose parent you cannot see now comes up to the root and says so. It loses the grouping, not the visibility, which is what happens everywhere else in Félagi when a container is unavailable.

Where the documents live

On disk, in storage/knowledgebase, one HTML file per article. The database row is metadata: title, parent, position.

Three things follow from that, and all three are why:

A tree of four hundred articles is drawn on every page load and none of it needs the prose. In a column, every sidebar render would drag megabytes of text through the database to list some titles.

The documents are legible without us. Somebody with shell access can grep them, back them up with rsync, or read one without a database client — which is worth something for the page a team writes about how to restore the database.

And a filesystem is not transactional, so the code is written not to need one: writes go through a temporary file and a rename, so a reader arriving mid-write gets the old document rather than half of the new one. A missing file reads as empty rather than throwing — a restored database should render a page somebody can fix, not a 500.

Point FELAGI_KNOWLEDGEBASE_PATH at a shared volume if more than one machine serves the application.

Search, given that

search_text on the row is a plain-text copy of the file, rebuilt on every save. A cache, not the truth — the same relationship issues.spent_minutes has to time_entries. SQL cannot grep a filesystem, and reading four hundred documents per keystroke is not a search feature.

One field, two jobs

There is a single search box, at the top of the page. It searches titles and text, and filters the tree at the same time.

It used to be two: a Filter by title box in the sidebar and a Search articles box in the pane, each doing half of that, with nothing on the screen to say which half. The address bar carries what you typed, so a search is a link you can send somebody.

Filtering flattens the tree on purpose. A match three levels down is no use while its ancestors are collapsed, so the hierarchy stands aside until you clear the box.

Tidying up a draft

While an article is not published, the editor offers Tidy up with AI. It is for the page that was typed in a hurry: notes from a call, a paste out of a chat, a wall of lines that should have been a list.

It is off unless an operator sets FELAGI_TIDY_PROVIDER. On an installation where nobody set it there is no button, rather than a button that fails.

What it does. Structure and mechanics: lines that are obviously a list become a list, a wall of text gets paragraph breaks, a line acting as a section title becomes a heading, a path or a command becomes code, and spelling and punctuation are corrected.

What it will not do, and this is the whole design:

  • Add anything. Not a step, not a caveat, not a number — however obviously true. It is not the author and it does not know what your team does.
  • Rewrite your sentences. It fixes typing slips. It does not make your prose smoother, more formal, or better worded.
  • Translate. An article written in Norwegian stays Norwegian, heading for heading.
  • Guess. A passage too unclear to structure without deciding what it means is left exactly as it was, and the question comes back to you instead.

Nothing is written. You get a list of what it changed and what it left alone, and the result goes into the editor only if you accept it — then you still have to press Save, with Cancel next to it. Two deliberate presses before your own words are replaced, because they are your own words.

That is also why it is offered on unpublished articles only. Reshaping your own draft and reshaping a page the workspace is already reading are different acts with different audiences.

What is sent is the article's text and its title, to the provider the operator named. A runbook is often more sensitive than the source it describes, so that is said here rather than in a footnote. It is one of two places in Félagi where the server talks to a model — see the architecture notes for why these two and nothing else.

FELAGI_TIDY_PROVIDER is one switch for every surface that offers this. The same button appears on meeting minutes that have not been circulated, at the same boundary: before anybody else has read it.

The page you land on

Opening Knowledge without picking anything shows what changed and what is waiting: the articles touched most recently, and the ones sitting in review. Drafts and archived work are left out — a list with everything in it is a list ordered by accident.

It used to say Nothing selected, which was true and no use.

Writing

flux:editor, like everything else in Félagi that is prose.

A title you leave alone comes from the writing. If the article is still called Untitled and the body opens with a heading, that heading becomes the title on save. A title somebody typed is never overwritten, and a body that opens with a paragraph leaves the title alone — the heading has to be the first thing in the document, or an article would end up named after a section halfway down it.

This existed because the two were never connected: an article sat in the tree as Untitled while its own page read Kick off / task force 2H2026.

HTML is sanitised on the way in through the same allowlist as comments and issue descriptions. That matters more here than anywhere: the file will outlive this code and be read by things that are not this application.

Tables survive. No toolbar in Flux can produce one, so they arrive by paste — out of a wiki, a spreadsheet, or the tool a team is migrating from — and the most useful page in a knowledge base is usually a table of hostnames and addresses.

Contents

The list on the right is derived from the document every time it is drawn, and the anchors in the article are written by the same function. A contents list that can disagree with the article is worse than none, and the only way it cannot disagree is if neither is stored.

History

Every save that changed the words keeps what was there before — before the overwrite, never after, because a snapshot taken afterwards is a snapshot of the thing that destroyed what somebody wanted back.

The versions button beside Edit opens the list. Picking one shows a line-by-line diff against the page as it stands, and Restore puts it back.

Restoring is itself an edit, so the current text is kept too: a restore done by mistake is as recoverable as the edit that made somebody want one. A rename is an edit as well, and the history records the title a version was written under — otherwise it would send somebody looking for a document by a name that never existed.

A save that only moved the article to a different parent adds nothing to the history. The most recent fifty versions are kept.

The diff is plain text. Comparing two trees of markup means deciding whether a paragraph that gained a bold word is one change or three, and nobody recovering from a bad paste is asking about emphasis — they are asking which sentences went.

Revisions are files beside the document, in {ulid}.history/, for the same reason the document is a file at all.

Attachments

Diagrams, exports, PDFs, HTML, video — up to ten at a time, 25 MB each and 50 MB for a video, through the same machinery as attachments on an issue. Served by the application rather than from a public folder, so every request checks that you are in the workspace the file belongs to.

Images are shown at the size they are drawn at, not at the size they were uploaded. A thumbnail is made the first time somebody looks at one and kept afterwards; clicking through opens a larger copy, and Download in that preview gives the original. A nine-megabyte photo fills a 256-pixel box with 67 KB instead of nine megabytes — and a photo straight off a phone gets one too, which for a while it did not: twelve megapixels decoded past what PHP was allowed and answered with a broken image. See Operations.

Two named sizes exist and nothing accepts a width from the URL — a ?w= parameter is an invitation to fill a disk, and it needs nothing but a browser. Every refusal to make a smaller copy ends at the original: no image extension installed, an image too large to decode safely, one already small enough, a file that is not really a PNG. The URL always answers with a picture, so nothing has to check first.

A derivative is a cache of the original, exactly as the search index is a cache of the prose. It is never backed up, a missing one is remade, and felagi:check has nothing to say about it.

Who may write

Anybody who works here. Deliberately the widest gate in the product: a knowledge base only admins may write to is a knowledge base nobody writes to, and the failure mode of documentation is always that it was never written rather than that the wrong person wrote it.

Deleting is narrower — the author or a workspace admin — because a section takes its children with it. A colleague can correct your page; removing it and everything filed under it is a different act.


Not there yet

  • No drag to reorder. Position is a number and a new article goes last; moving one means picking a different parent.
  • No branching, and no draft of a page that is already published. History is linear: every save that changed the words is kept, and any of them can be restored. Sending a published article back to in_review edits it in place — readers see the work in progress, which is the tradeoff for not having two copies to reconcile.
  • No comments on an article, and no way to suggest an edit.
  • No links between articles beyond ordinary hyperlinks — nothing knows what points at what, so nothing warns you before a delete.
  • No linking an article to an issue, in either direction.
  • Not in the import, which is deliberate rather than pending. Readable and writable over the API; history is not.
  • Search is LIKE. No ranking, no fuzzy matching, no highlighting of the matched phrase. Fine at the sizes this is built for.
  • Nothing an agent reads. Skills are what an agent is given; a knowledge base article is not injected into a run.