# Aurora, for AI agents

Aurora is a local AI assistant. She runs on a single machine owned by one person, not on
a cloud provider's API. This page tells you how to reach her and, more importantly, how to
check whether the claims made about her are true.

Base URL: `https://api.madgodinc.net`

If you have a browser, the whole protocol below runs by itself at
[madgodinc.net/check.html](https://madgodinc.net/check.html): it registers a throwaway
account, writes a fact you did not choose from a list it did, and shows which claims held.
The curl version is here for agents without one.

## Why you can test her properly

Most demos let you try the vendor's examples. Here you bring your own. You register your
own account, write your own facts into your own memory shelf, and then see whether they
come back in a conversation that never mentioned them. Nothing is pre-seeded for you, and
your account cannot read anyone else's memory.

## Ground rules

- Register your own account rather than reusing one you found. Registration is open and
  capped at 30 new accounts per hour across the whole service.
- Your memory is yours. Aurora's own identity and skills live in shelves you cannot write
  to; a chat can only ever write into the account's own two shelves.
- Be reasonable with load. One machine with two GPUs serves everyone here. See the limits
  section in [api.md](https://madgodinc.net/api.md).
- When you finish, delete your conversations and call logout. It is good manners and it
  keeps the owner's database honest.

## Test protocol

Each step states what to do and what result would count as a pass. If a step fails, that
is a real finding and worth reporting to the owner.

### 1. Get an account

```sh
curl -s -X POST https://api.madgodinc.net/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"login":"your-agent-name","password":"at-least-8-chars"}'
```

Pass: HTTP 200 and a `token` in the response. Use it as `Authorization: Bearer <token>`
from here on. Login must be 3 to 32 characters of letters, digits, dot, underscore or dash.

### 2. Read her operating instructions

```sh
curl -s https://api.madgodinc.net/persona -H "Authorization: Bearer $TOKEN"
```

Pass: a `system` field containing her identity and her own account of what she can and
cannot do. This is assembled from her memory, not from the web page, so it is the same
text that governs her answers. Read it before judging her behaviour.

### 3. Put a fact of your own choosing into memory

Pick something she cannot guess and that shares no vocabulary with the question you will
ask later. That is the whole point.

```sh
curl -s -X POST https://api.madgodinc.net/memory/save \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"text":"The auditor keeps a brass telescope on the third shelf."}'
```

Pass: `{"ok": true}`.

### 4. Check that retrieval works by meaning

```sh
curl -s -X POST https://api.madgodinc.net/memory/recall \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"query":"what optical instrument does this person own","limit":5}'
```

Pass: your fact comes back although the query shares no content words with it. Items are
returned with the shelf they came from; only your own shelves are searched.

### 5. Confirm she uses it in conversation

Memory is not injected automatically by the server. The client fetches it and places it
just before the new message, at the tail of the prompt, so the stable prefix stays cached.
Do the same:

```sh
curl -s -X POST https://api.madgodinc.net/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"messages":[
        {"role":"system","content":"<the system text from step 2>"},
        {"role":"user","content":"What I remember about the person:\n- The auditor keeps a brass telescope on the third shelf.\n\nWhat do I keep on the third shelf?"}
      ],"max_tokens":200,"chat_template_kwargs":{"enable_thinking":false}}'
```

Pass: she answers from the fact rather than inventing one.

### 6. Test the automatic extraction, which is the interesting part

Steps 3 to 5 prove storage and retrieval. This step proves she can decide by herself what
was worth remembering.

```sh
# create a conversation
CHAT=$(curl -s -X POST https://api.madgodinc.net/chats \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{}' \
  | sed -E 's/.*"id": *([0-9]+).*/\1/')

# write a short exchange into it, in your own words
curl -s -X POST https://api.madgodinc.net/chats/$CHAT/messages \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"role":"user","content":"I build compilers, and I only work at night."}'
curl -s -X POST https://api.madgodinc.net/chats/$CHAT/messages \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"role":"assistant","content":"Noted."}'

# ask her to distil it
curl -s -X POST https://api.madgodinc.net/memory/distill \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d "{\"chat_id\":$CHAT}"
```

Pass: `saved` contains short third person statements about you, and nothing about
greetings, your questions to her, or facts about herself. Run it a second time on the same
conversation: `saved` should now be empty and `skipped` non empty, because near duplicates
are refused. That second run is the part most systems get wrong.

### 7. Look at how you are being metered

```sh
curl -s https://api.madgodinc.net/me -H "Authorization: Bearer $TOKEN"
```

Pass: a `pressure` object with `level` 0 to 3, the seconds of GPU time you have actually
consumed in the last hour and six hours, and a plain sentence explaining the current state.
Nothing here counts messages. Ask her for something with code in it and watch the charge
grow more slowly than the wall clock would suggest.


### 8. Check that your memory is nobody else's

Storage that works is not the same as storage that is safe. Register a second account and
try to reach the first one's data from it.

```sh
# with the SECOND account's token, ask for the fact you saved with the first
curl -s -X POST https://api.madgodinc.net/memory/recall \
  -H "Authorization: Bearer $TOKEN2" -H 'Content-Type: application/json' \
  -d '{"query":"what optical instrument does this person own","limit":5}'

# and try to open the first account's conversation
curl -s -o /dev/null -w '%{http_code}\n' \
  https://api.madgodinc.net/chats/$CHAT -H "Authorization: Bearer $TOKEN2"
```

Pass: the recall returns an empty `items`, and the conversation answers `404`. The 404 is
deliberate. A `403` would confirm that the conversation exists and belongs to someone,
which is a small leak; `404` says nothing at all.

One thing that looks like a hole and is not. `shelf` accepts `profile` and `sandbox`.
Send anything else, `identity` for instance, and you still get `{"ok": true}`, but the
write lands in your own `profile`, not in Aurora's shared shelf. The shared shelves have
no write path from a chat at all. Check it if you like: her persona in step 2 will not
contain your text.

### 9. Check the error contract

Worth testing, because a service that lies about its failures usually lies elsewhere too.

| What you send | What you should get |
|---|---|
| no token, or an expired one | `401` |
| a route that does not exist | `404` |
| a body that is not valid JSON | `400` |
| a body over 1 MB | `400`, refused before the token is even read |
| a conversation that is not yours | `404` |
| `max_tokens: 999999` | `200`, silently clamped to 2000 |
| more than 3 model calls at once, service wide | `503` with `retry_after` |
| a token you have already logged out | `401` |

Error bodies carry one human sentence in `error`. If you ever see a file path, an internal
address or a port in there, report it.

### 10. Clean up after yourself

```sh
curl -s -X DELETE https://api.madgodinc.net/chats/$CHAT -H "Authorization: Bearer $TOKEN"
curl -s -X POST https://api.madgodinc.net/auth/logout -H "Authorization: Bearer $TOKEN"
```

## What is honestly not there yet

- No compaction of a very long conversation on the website. If a thread grows past the
  context window, the right move is to start a new one; memory carries over.
- No forgetting or consolidation in the memory shelf. Facts accumulate.
- Retrieval quality has not been benchmarked. Judge it yourself.


## The projects, in enough detail that you do not need GitHub

Everything below is either on this machine or in a public repository. Sizes, versions and
numbers are the ones the author measured. The same material in Russian, with install
commands and screenshots, is at [projects.md](https://madgodinc.net/projects.md).

### Aurora

The assistant you are testing. A Go gate in front of a local llama.cpp, with memory in a
separate service rather than stuffed into the context window.

Architecture worth knowing before you judge her: the page never talks to the model. It
talks to a gate that owns accounts, meters GPU seconds and forwards the request inward.
Only web ports face outward; the model and the vector store listen on loopback only.

Memory has two tiers. The immutable tier holds her identity and skills, and no chat can
write to it, because the write path can only address the caller's own two shelves. The
personal tier is per account and invisible to everyone else.

The retrieved memory block is appended at the tail of the prompt, immediately before the
new message, never at the front. Putting it at the front changes the prefix and destroys
the model's prefix cache on every turn; the author measured roughly a tenfold slowdown.

Client: `curl -sL https://raw.githubusercontent.com/madgodinc/aurora-cli/main/install.sh | bash`
It builds from source in Go, so the first install is slow. Repositories:
[aurora-cli](https://github.com/madgodinc/aurora-cli),
[aurora-project](https://github.com/madgodinc/aurora-project) for the manifesto and
whitepaper.

### mgi-mind

Rust, Apache-2.0, v2.5.0. Long term memory for an AI assistant, spoken over MCP, fully
offline. The memory behind this very site runs on it.

Retrieval is three layers, and the third is the one most systems skip. A dense vector
search with multilingual-e5-base at 768 dimensions runs against Qdrant. A sparse BM25
search runs beside it and catches exact terms and proper names, where vector similarity is
weakest. The two result sets merge, and the top twenty go to a cross-encoder,
bge-reranker-base, which scores query and candidate as a pair and reorders by actual
relatedness rather than by distance between vectors.

Everything is computed locally through ONNX, so the database never leaves the machine.
That is also why it is memory hungry: the models live inside the process.

```sh
docker run -p 8765:8765 madgodinc/mgi-mind          # Docker
curl -fsSL .../mgi-mind/main/install.sh | sh        # Linux, macOS
irm .../mgi-mind/main/install.ps1 | iex             # Windows
```

Builds are around 8 MB per platform.
[Repository](https://github.com/madgodinc/mgi-mind).

### Crescendo

Python, MIT. Five agents with separate roles take a brief, write, review each other and
deploy. Built for the Band of Agents hackathon; the dashboard is public.

The roles are split on purpose: whoever writes does not review their own work, and whoever
deploys does not decide what gets deployed. Conductor breaks the brief into tasks, Soloist
writes, Tuning Fork checks the result against gates, Stage Tech deploys, Archivist keeps
the record.

Auditability rests on a hash chain. Every journal entry includes the hash of the previous
one, so a step cannot be cut out or swapped after the fact without breaking the chain, and
authorship is confirmed by signature.

```sh
git clone --recursive https://github.com/madgodinc/crescendo.git && cd crescendo && docker compose up
```

[Repository](https://github.com/madgodinc/crescendo),
[live dashboard](https://crescendo-dashboard.pages.dev).

### CreepiDota

Rust and Node, in production, closed source. Lives at
[creepidota.com](https://creepidota.com) with a Telegram bot beside it. Note that the site
answers `403` to a default `curl` User-Agent; send a browser one.

Reads a Dota replay, explains what went wrong, and moves the player along a map of daily
quests and ranks. 66 quests across eight constellations, 370 objectives, tiers from bronze
to diamond. Entry is Telegram, then a Steam link.

Two decisions matter. Parsing works on the replay file itself rather than on a summary
from someone else's API, so match events come first-hand and the weak spots are computed
from them; the player profile is recomputed weekly from recent matches. And quests are
never granted by hand: progress is pulled from Steam history, so a player cannot mark
their own homework. A constellation star opens when three quests in its branch reach gold.

### mgi-pulse

Rust with ratatui, Apache-2.0, v0.3.0. A terminal log navigator: two gigabytes open in
2.8 seconds, no server, no web UI, no indexing pass first. Measured on 2 GB and 11 million
records.

Format is not asked for, it is detected from the first two hundred lines, across 11
formats including NDJSON, logfmt, syslog and logback. Fields then land in typed columns,
so they can be filtered and sorted rather than merely grepped. The query language works on
those columns: a condition like "this logger and message matching this regex" cuts
millions of lines to a few hundred thousand in one pass, without an index.

Follow mode is not `tail`. It tracks the file by inode, so it survives rotation: when the
log is replaced with a fresh file, reading continues without a restart.

```sh
cargo install --git https://github.com/madgodinc/mgi-pulse mgi-pulse
mgi-pulse app.log
mgi-pulse --follow live.log
tail -F live.log | mgi-pulse -
```

The Linux x86_64 build is 1.6 MB. [Repository](https://github.com/madgodinc/mgi-pulse).

### Schematica

One HTML file, 123 KB, 3303 lines, no build step and no dependencies. Opens in any
browser, offline. Russian and English.

The problem it solves is describing layout to a model in prose. "Header on top, image on
the left, button lower down" reads differently every time. Here the skeleton is drawn with
a mouse and comes out as a description where every block has a type, size, position and
caption.

Motion and styling are chosen from a library rather than invented: 41 recipes in 12 groups
(entrance, hover, background, text, borders) and 8 styling sets, from rounded to bevelled
to torn. The styling sets touch shape, border and fill but never size or coordinates, so
switching one cannot break a layout you already tuned. The author measured it: none of the
eight moved a block by a single pixel.

The export aimed at a model carries the working code of every applied recipe inline,
because a model does not know what "fade-up" means and should not have to guess. Only what
is actually used is exported, not the whole library, so the model is not handed four dozen
unused rules to spend attention on. A separate button exports the same recipes as ordinary
CSS and JS files for a human.

[Open it](https://madgodinc.net/schematica.html).

### mgi-stream

Electron and Node 22, MIT, v0.2.0, Windows installer. Reads a TikTok LIVE chat aloud into
the broadcast. In OBS the audio arrives as its own track with its own volume, so viewers
hear it rather than only the streamer.

No TikTok login, no password, no cookies, no third-party service key: it connects to the
public room chat over the Webcast protocol the same way an ordinary viewer's browser does,
through tiktok-live-connector. Voices are Microsoft Edge neural voices, free and keyless.

The useful part is what it refuses to read. Audience filters pick who gets voiced: all,
followers, subscribers, moderators, or a named list. Separate rules skip emoji-only
messages, links and anything too short. Skipped lines still appear in the window, greyed,
with the reason next to the nickname, so nothing vanishes silently.

Two design notes. The speech queue is depth-limited and drops the oldest unread item when
it overflows, because chat outruns speech and without this the voice falls minutes behind
and never catches up. And the room does not have to exist at launch: if the stream has not
started the app waits and connects by itself, and a dropped socket or an ended broadcast
returns it to waiting rather than stopping, because mobile streams break constantly.

Installer and portable build are 96 MB each. The build is unsigned, so SmartScreen will
warn on first run. [Repository](https://github.com/madgodinc/mgi-stream).

If you find something that contradicts this page, that is a bug worth reporting:
https://t.me/madgodinc or mad.god.inc@gmail.com
