Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

[![Release](https://img.shields.io/github/v/release/cortexapps/engrams?filter=v*&label=release)](https://github.com/cortexapps/engrams/releases/latest)

engrams is a self-hosted orchestrator for AI coding agents. It runs each agent in its own [Firecracker](https://github.com/firecracker-microvm/firecracker) microVM, snapshots the VM when the agent goes idle, and restores it when the next prompt arrives.
engrams is the open source, self-hosted software factory, by [Cortex](https://www.cortex.io). It is an open source alternative to Devin and Factory.ai that runs in your own cloud. It runs Claude Code, Codex, or your own agent, each in its own [Firecracker](https://github.com/firecracker-microvm/firecracker) microVM, snapshots the VM when the agent goes idle, and restores it when the next prompt arrives.

A session is a plain OCI image plus an agent harness. You build the image with `docker build`, enable it once, and engrams boots it on a host in your own cloud, streams the transcript and tool calls to the dashboard, Slack, or the CLI, and snapshots the VM away when it goes quiet. Claude Code and Codex ship as harnesses. The pattern is the one behind hosted sandbox products like E2B, Modal Sandboxes, and Ramp's Inspect; engrams is the version you run yourself.
A session is a plain OCI image plus an agent harness. You build the image with `docker build`, enable it once, and engrams boots it on a host in your own cloud, streams the transcript and tool calls to the dashboard, Slack, or the CLI, and snapshots the VM away when it goes quiet. Claude Code and Codex ship as harnesses. The sandbox layer is the pattern behind hosted products like E2B, Modal Sandboxes, and Ramp's Inspect; engrams is the version you run yourself.

Docs: [engramsfactory.com/docs](https://engramsfactory.com/docs/). For agents: [llms.txt](https://engramsfactory.com/llms.txt), and [install engrams with a coding agent](https://engramsfactory.com/docs/getting-started/install-with-an-agent/).

![A session in the engrams dashboard. Asked for a PNG of a pelican riding a bicycle with no image libraries and no internet, the agent wrote a PNG encoder in Python and shared the file in the thread.](site/src/assets/screenshots/session-pelican.png)

Expand Down
2 changes: 1 addition & 1 deletion site/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default defineConfig({
integrations: [
starlight({
title: "engrams",
description: "Self-hosted sandboxes for AI coding agents, on Firecracker microVMs.",
description: "engrams is the open source, self-hosted software factory. It runs coding agents in Firecracker microVMs in your own cloud.",
logo: { src: "./src/assets/engram-trace.svg", alt: "" },
favicon: "/favicon.ico",
head: [
Expand Down
7 changes: 6 additions & 1 deletion site/src/components/starlight/Head.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
---
// Starlight's head tags, then the tag manager loader.
// Starlight's head tags, a link to the page's Markdown, then the tag manager loader.
import Default from "@astrojs/starlight/components/Head.astro";
import GoogleTagManager from "../GoogleTagManager.astro";

const { entry } = Astro.locals.starlightRoute;
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
const markdown = new URL(`${base}/${entry.id}.md`, Astro.site);
---

<Default />
<link rel="alternate" type="text/markdown" href={markdown} />
<GoogleTagManager part="head" />
31 changes: 27 additions & 4 deletions site/src/components/starlight/PageTitle.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
// The article masthead: breadcrumb, title, description, and a meta strip with
// the read time and the edit link. The docs home gets the hero variant instead:
// the read time, the page as Markdown, and the edit link. The docs home gets the hero variant instead:
// an eyebrow, the title, and the description as a lede.
// Starlight anchors the page title at #_top; the skip link and the TOC rely on it.
import { VERSION_LABEL } from "../../version";
Expand All @@ -27,6 +27,7 @@ const crumbs = trail(sidebar) ?? [];
const words = (entry.body ?? "").split(/\s+/).filter(Boolean).length;
const minutes = Math.max(1, Math.round(words / 200));
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
const markdown = `${base}/${entry.id}.md`;
---

{
Expand All @@ -53,10 +54,32 @@ const base = import.meta.env.BASE_URL.replace(/\/$/, "");
{entry.data.description && <p class="doc-desc">{entry.data.description}</p>}
<div class="doc-meta">
<span>≈ {minutes} min</span>
{editUrl && (
<a href={editUrl.href} class="edit">edit on github ↗</a>
)}
<span class="doc-links">
<a href={markdown}>markdown</a>
<button type="button" data-copy-markdown={markdown}>copy markdown</button>
{editUrl && (
<a href={editUrl.href} class="edit">edit on github ↗</a>
)}
</span>
</div>
</div>
)
}

<script>
// Copy the page's Markdown, for pasting into an agent or a chat.
for (const button of document.querySelectorAll<HTMLButtonElement>("[data-copy-markdown]")) {
button.addEventListener("click", async () => {
const label = button.textContent;
try {
const res = await fetch(button.dataset.copyMarkdown!);
if (!res.ok) throw new Error(String(res.status));
await navigator.clipboard.writeText(await res.text());
button.textContent = "copied";
} catch {
button.textContent = "copy failed";
}
setTimeout(() => (button.textContent = label), 2000);
});
}
</script>
6 changes: 4 additions & 2 deletions site/src/content/docs/docs/getting-started/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ migrating a session. A cold start from an image's base snapshot is under a secon
pre-warmed pool. These are the numbers from the reference deployment; measure your own fleet
before you promise them to anyone.

## What is the license?
## Is it open source?

engrams is free software under the GNU Affero General Public License v3.0.
Yes. The whole system is open source on [GitHub](https://github.com/cortexapps/engrams): the
control plane, the host agent, the dashboard, the CLI, and the Firecracker fork. You can read,
change, and run every line.
169 changes: 169 additions & 0 deletions site/src/content/docs/docs/getting-started/install-with-an-agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
title: Install engrams with a coding agent
description: Exact steps, checks, and stop conditions for a coding agent that installs engrams on one machine.
sidebar:
order: 2
---

This page is for a coding agent, such as Claude Code, Codex, or Cursor, that a person asked to
install engrams. Every step has a command and a check. Do the steps in order, and do not go to
the next step until the check passes. A person can read it too; it is the same install as
[Run engrams locally](../local-quickstart/), written for a machine to follow.

The result is the whole engrams stack on this machine: Postgres, a container registry, a blob
store emulator, the control plane, a host agent, and the dashboard, with one enabled image and
one test session that ran a command. It is for evaluation, not production. For a production
install in a cloud account, stop and give the person
[Deploy to your cloud](../../guides/deploy-overview/): it creates cloud resources and needs
their decisions.

## Rules for the agent

- Do not create, guess, or copy model API keys. The last step asks the person for one.
- Do not run `sudo` without asking the person. Group changes such as adding the user to `kvm`
need their approval.
- `just dev` runs until it is stopped. Start it as a background process and keep its log.
- The first start compiles Rust and can take 10 to 20 minutes. A long wait is not a failure.
Read the log before you decide that something is stuck.

## 1. Check the machine

engrams runs on macOS on Apple Silicon and on Linux (x86_64 or arm64). Windows is not
supported; use WSL 2 with a Linux distribution.

```sh
uname -sm
docker info --format '{{.ServerVersion}}'
```

Check: `uname` prints `Darwin arm64`, or `Linux` with `x86_64` or `aarch64`. `docker info`
prints a version. If `docker info` fails, Docker is not running: ask the person to start Docker
Desktop, OrbStack, or Colima.

On Linux, check whether sessions can use Firecracker:

```sh
[ -r /dev/kvm ] && [ -w /dev/kvm ] && echo KVM-OK || echo KVM-NO
```

`KVM-NO` is not a stop. The stack still runs, but sessions run as plain subprocesses with no
isolation. Tell the person, and say that adding their user to the `kvm` group fixes it.

## 2. Get the source and the toolchain

```sh
git clone https://github.com/cortexapps/engrams.git
cd engrams
command -v nix && echo NIX-OK || echo NIX-NO
```

With `NIX-OK`, run every later command inside the pinned toolchain. Put `nix develop -c` in
front of each one, for example `nix develop -c just bootstrap`. The first `nix develop`
downloads the toolchain and takes several minutes.

With `NIX-NO`, install the toolchain. On macOS:

```sh
# Rust from https://rustup.rs. The repository's rust-toolchain.toml picks the version.
rustup target add aarch64-unknown-linux-musl
brew install just tilt-dev/tap/tilt jq squashfs protobuf pkg-config openssl node pnpm bun
brew install FiloSottile/musl-cross/musl-cross --with-aarch64
xcode-select -p
```

On Linux you need Rust from rustup, Docker, `just`, `tilt`, `jq`, and `bun`.

Check:

```sh
just --version && tilt version && bun --version && cargo --version
```

Each command prints a version.

## 3. Create the local key and fetch the guest kernel

```sh
just bootstrap
just pull-kernel
```

Check: both exit with code 0, and `.env` contains `ENGRAM_KEK_MASTER_KEY`. These steps run
once per machine. Running them again is safe.

## 4. Start the stack

Start it in the background and write its log to a file:

```sh
mkdir -p var
nohup just dev > var/just-dev.log 2>&1 &
```

Wait until the orchestrator answers and the local admin key exists:

```sh
until curl -fsS http://127.0.0.1:8787/healthz > /dev/null && [ -s var/dev-api-key ]; do
sleep 15
done
curl -fsS http://127.0.0.1:8787/healthz
```

Check: the last command prints a JSON body with `"ok":true`. Put a limit of 30 minutes on the
wait. If the limit passes, read `var/just-dev.log`, and run `tilt get uiresource` to see which
resource failed. `tilt logs <resource>` prints the log of one resource.

The first lines of the log say which backend the stack picked: Firecracker when `/dev/kvm` is
usable, Apple Virtualization on an Apple Silicon Mac, subprocesses otherwise.

## 5. Enable the demo image

```sh
just bake-demo-enable
```

This builds the image in `deploy/demo/`, pushes it to the local registry as
`localhost:5001/demo:warm-1`, enables it, and waits while engrams captures its base snapshot.
It also builds the `engrams` CLI to `target/debug/engrams`.

Check: the command exits with code 0. A non-zero exit prints the failing stage and the end of
its output.

## 6. Prove that a session runs

Start a session with no agent, run a command in it, and delete it:

```sh
export ENGRAMS_URL=http://127.0.0.1:8787
export ENGRAMS_API_KEY="$(cat var/dev-api-key)"
SID=$(target/debug/engrams session create --image localhost:5001/demo:warm-1 --dev-vm)
target/debug/engrams session exec "$SID" 'echo engrams-ok; uname -a'
target/debug/engrams session delete "$SID"
```

Check: `session exec` prints `engrams-ok` and exits with code 0. The install is complete. This
check needs no model key, because `--dev-vm` starts the VM with no agent in it.

## 7. Hand off to the person

Tell the person these things:

- The dashboard is at http://localhost:5173. Registration is open in the local stack, so they
can create an account there.
- To let an agent work, they add a model credential in the dashboard under Settings: their
Claude Code token, or an org secret named `ANTHROPIC_API_KEY` (or `CODEX_API_KEY` for Codex).
- [Your first session](../first-session/) shows how to start an agent, watch it, and see it
snapshot and resume.
- `just dev-down` stops the stack and keeps its data. The next `just dev` starts where it
stopped.

## If a step fails

| Symptom | What to do |
|---|---|
| `docker info` fails | Docker is not running. Ask the person to start it. |
| `just: command not found` with Nix | Put `nix develop -c` in front of the command. |
| The health wait passes 30 minutes | Read `var/just-dev.log` and `tilt get uiresource`; report the failed resource and its log to the person. |
| Port 8787, 5173, or 10350 is in use | Another stack, or an old one, is running. Run `just dev-down`, or ask the person what owns the port. |
| `bake-demo-enable` fails at capture | Read the printed stage. On Linux without KVM, check that the log says subprocesses were picked. |
| Sessions run without isolation | `/dev/kvm` is not usable by this user. Tell the person; do not fix group membership without approval. |
5 changes: 3 additions & 2 deletions site/src/content/docs/docs/getting-started/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ sidebar:
order: 1
---

engrams is an open-source system that runs AI coding agents in isolated microVMs on your own
infrastructure. It gives people one place to direct their work: start a task, inspect its
engrams is the open source, self-hosted software factory. It runs AI coding agents in isolated
microVMs on your own infrastructure, as an open source alternative to Devin and Factory.ai. It
gives people one place to direct their work: start a task, inspect its
changes, and continue the conversation from the dashboard or Slack. Turn repeat work into an
automation that starts on a schedule or an event.

Expand Down
7 changes: 5 additions & 2 deletions site/src/content/docs/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,8 @@ import HudCard from "../../../components/docs/HudCard.astro";
</HudCard>
</div>

If you are reading these docs as an agent, every page is Markdown in the repository under
`site/src/content/docs/docs/`, and the [source on GitHub](https://github.com/cortexapps/engrams/tree/main/site/src/content/docs/docs) is the same text as this site.
If you are reading these docs as an agent, every page is also plain Markdown: put `.md` in
place of the trailing slash, or use the copy markdown button on the page. The index of every
page is at [llms.txt](../llms.txt), and all the docs in one file are at
[llms-full.txt](../llms-full.txt). To install engrams, start with
[Install engrams with a coding agent](getting-started/install-with-an-agent/).
2 changes: 1 addition & 1 deletion site/src/landing/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Each section connects an outcome to the product behavior that makes it possible.

export const hero = {
eyebrow: "Stand up a software factory of your own",
eyebrow: "The open source, self-hosted software factory",
title: "The open source alternative to",
titleAccent: "Devin.",
lede: "engrams runs Claude Code, Codex, or your own agent in an isolated microVM on your infrastructure. Start one from a prompt, a schedule, a pull request, or a Slack thread. Walk away mid-task, and pick it up days later with its files and history intact.",
Expand Down
10 changes: 7 additions & 3 deletions site/src/layouts/Marketing.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ interface Props {
/** The landing page runs its own cover hero under the header and its own
* Start band above the footer, so it asks the layout to leave both edges open. */
landing?: boolean;
/** schema.org JSON-LD for the page, so search engines and assistants read
* what the product is and who makes it from the page itself. */
structuredData?: Record<string, unknown>;
}
const { title, description, landing = false } = Astro.props;
const { title, description, landing = false, structuredData } = Astro.props;
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
const canonical = new URL(Astro.url.pathname, Astro.site);
// In the header on wide screens, and in the menu on narrow ones.
Expand Down Expand Up @@ -53,9 +56,10 @@ const ogImage = new URL(`${base}/og.png`, Astro.site);
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="engrams: the open source alternative to Devin." />
<meta property="og:image:alt" content="engrams: the open source, self-hosted software factory." />
<meta name="twitter:card" content="summary_large_image" />
<meta name="theme-color" content="#081A1B" />
{structuredData && <script type="application/ld+json" set:html={JSON.stringify(structuredData)} />}
</head>
<body>
<GoogleTagManager part="body" />
Expand Down Expand Up @@ -120,7 +124,7 @@ const ogImage = new URL(`${base}/og.png`, Astro.site);
<a class="btn ghost" href="https://www.cortex.io">Visit cortex.io <span>→</span></a>
</div>
<div class="row">
<span>engrams · open source</span>
<span>engrams · the open source, self-hosted software factory</span>
<nav aria-label="Footer">
<a href={`${base}/architecture/`}>Architecture</a>
<a href={`${base}/compare/`}>Compare</a>
Expand Down
Loading
Loading