oficina / memoria Public

Clone
git clone https://amadan.net/oficina/memoria

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– memoria
2
3The Oficina suite's memory. One instance per team.
4
5Ask a question, get an answer grounded in your team's own documents β€”
6with a citation you can open, always.
7
8**What exists today is a search engine, not an assistant**, and it has
9exactly one model call in it, **switched off unless you switch it on**.
10With it off β€” the default β€” the trust claim is as strong as it gets:
11
12> memoria's passages and its index are sealed at rest under this team's
13> own key, and **no document ever leaves this instance** β€” not at query
14> time, and not at indexing time, because nothing is sent anywhere to be
15> indexed.
16
17**With `MEMORIA_AI=on`, one thing changes, and this is all of it.** When
18a search asks for a postal address β€” "office address", "postal address" β€”
19memoria first looks for one with its own checks. Addresses have no
20checksum, so those checks cannot find one. It then sends **up to eight
21passages per search request** β€” the top eight that search returned,
22passages you were already allowed to read β€” to **TensorX**, the one named
23provider, which points at any addresses in them. **That includes the live
24searches that run while you type**, not only the one you submit: each
25pause in typing a query that asks for an address is its own request.
26memoria shows only text it finds byte-for-byte in those passages, marks
27each such row πŸ€– with TensorX and the model's name, and never shows the
28model's own words. Nothing else is sent: not
29the rest of your documents, not your query's other results, and nothing
30at all for phone numbers, eircodes, IBANs, VAT numbers or email
31addresses, which memoria checks itself. TensorX can read what it
32receives; we rely on its retention terms and cannot verify them.
33
34An earlier draft of the first sentence said "nothing leaves this
35instance", which was false and a code review caught it. Signing in fetches
36`https://<your address's domain>/.well-known/keymail` **before** anyone is
37admitted, so an anonymous visitor can make the instance dial any domain
38they type. No document leaves; a DNS name does.
39
40**The full assistant changes this again, by much more**, and the design
41spec is explicit about how: embeddings come from a hosted API, so
42switching it on will send **every document in the corpus** to the named
43provider once, at indexing time β€” not just the passages behind an answer.
44That is not built. When it is, this paragraph is replaced by Β§3.6 of the
45design, and not before: Β§3.6 describes egress this binary does not yet
46perform, and saying it early would overstate what leaves as surely as
47the old sentence here understated it.
48
49## Live
50
51**https://memoria.oficina.works** β€” account `bds`, channel `edge`,
52serving `71bebef`. Sign in with `paul@keymail.dev`, `paul@rslw.com` or
53`paul@tito.io`; real email goes out through the platform's SES, so no
54sign-in link is ever written to a log.
55
56It starts **empty**. Paste or upload on **Add content** (`/add`, linked
57from the front page) to fill it β€” the
58seed tool needs filesystem access to the database, so it only works
59against a local instance.
60
61The origin moved here from `memoria.bds.oncarlos.com` on 2026-09-04, once
62the certificate issued β€” the move `docs`, `sheets` and `calendar` had
63already made, along with `id.oficina.works` and the apex. It was
64`MEMORIA_ORIGIN` and a restart and nothing else: no hostname is compiled
65into this binary.
66
67`memoria.bds.oncarlos.com` still answers. It is the platform route the
68custom hostname sits in front of, not a second deployment. Signing in
69there still *starts* β€” but it does not leave you signed in there, because
70`MEMORIA_ORIGIN` is the base of every emailed link, so the link lands you
71on `oficina.works` and the session cookie is set for that host. Use the
72canonical name.
73
74That value is also the CSRF yardstick and the OAuth `client_id` keymail
75validates redirects against, so it must match the host the browser is
76actually on. Be precise about how much the CSRF half is doing, though:
77`csrf.SameOrigin` reads `Sec-Fetch-Site` **before** it compares origins,
78so a browser sitting on the old hostname sends `same-origin` and passes.
79What refuses is a scripted post carrying a stale `Origin` header. The
80mailed link, not the CSRF check, is what actually funnels people onto one
81host.
82
83**The DEK is the whole corpus.** It lives in `carlos secrets` for this
84app, and a copy is at `memoriadrive/DEK-production.txt`. Lose both and the
85data is unreadable β€” exactly as with `SHEETS_DEK`.
86
87## Trying it locally
88
89```
90cd /home/paulca/memoriadrive
91./run.sh
92```
93
94`run.sh` sets `MEMORIA_ALLOW_LOGGED_MAIL=1`, which is a **local-dev-only**
95opt-in: without SMTP configured, rastrillo logs the magic link instead of
96mailing it, and that link is a live credential. The app now refuses to
97start without either real SMTP or that explicit flag, because the default
98had been to log sign-in credentials silently.
99
100Then open **http://127.0.0.1:8823** and sign in. Sign-in goes through
101keymail, so use an address on the owners list in `run.sh`
102(`paul@tito.io` or `paul@keymail.dev` as it stands).
103
104It comes pre-seeded with 55 documents and 649 passages β€” every `.md` in
105the `oficina` tree, so the first thing you can search is the suite's own
106specs. Try `sealed at rest`, `grants join`, `revoke`, `escritorio`,
107`plausible empty list`.
108
109Things worth poking at, because each is a decision rather than an
110accident:
111
112- Search **ORs** its terms. Ask it a question in words β€” "what is the
113 worst available failure shape" β€” and it works. ANDing would return
114 nothing, and nothing is indistinguishable from having nothing.
115- A no-match page tells you how much *is* indexed, so an absent answer
116 never looks like an empty index.
117- Every result cites a document and a passage number, and the passage
118 anchors when you follow it.
119
120### Adding your own
121
122Paste or upload text on **Add content** (`/add`), or seed a directory:
123
124```
125cd /home/paulca/memoriadrive
126MEMORIA_DEK="$(cat DEK.txt)" ./seed --db m.db --dir /path/to/notes --source notes
127```
128
129The seed tool uses the same store, chunker and sealing as the app. It is
130the honest ancestor of slice 3's connectors, and it bypasses nothing β€”
131without the right DEK it refuses to run.
132
133### The DEK
134
135`DEK.txt` in `memoriadrive` is the key everything is sealed with. **Lose
136it and the corpus is gone**, exactly as with Sheets' `SHEETS_DEK`. The
137app never generates one: a generated fallback would seal this boot's data
138under a key the next boot cannot derive, losing the data quietly instead
139of refusing loudly.
140
141## Deploying
142
143```
144make release
145carlos deploy --app memoria --account bds --channel edge --timeout 5m releases/memoria-linux-arm64
146```
147
148Trust the command `make release` prints over the one written here: it
149names the path it actually built. This section used to say
150`./releases/memoria-arm64`, which no build has ever produced β€” the binary
151is named for its target, `linux-arm64`.
152
153`carlos deploy` refuses to run when the tree is unclean, and it counts
154untracked files. A stray editor directory or dotfile in the checkout is
155enough to stop it, with a message about the version defaulting to the git
156sha. When the tracked tree is genuinely clean, pass the sha `make release`
157stamped β€” `--version $(git rev-parse --short HEAD)` β€” rather than
158committing noise to get past it.
159
160Flags go **before** the binary path; Go's flag package stops parsing at
161the first non-flag argument, so a path first makes `--account` invisible.
162`carlos deploy`, never `carlos ship` on its own. Then poll
163`GET /api/version` rather than trusting `X-Carlos-Version`, which is the
164router's adopted release and flips before the process cycles.
165
166## The design
167
168`docs/superpowers/specs/2026-09-03-memoria-design.md`. Draft 3, which
169answers independent reviews of drafts 1 and 2 β€” four passes across Fable
170and GPT Sol, every one returning REQUEST CHANGES. Β§1.2 records what changed and why β€” the
171central correction being that authorization is asked of whichever app
172owns a document, per person, per query, rather than derived from a
173membership table memoria keeps.
174
175`QUESTIONS-FOR-PAUL.md` is what I could not decide alone.
176
177## What is not here
178
179No assistant, no embeddings, no sibling apps, no external connectors, no
180snapshot of the index (it rebuilds at boot β€” milliseconds at this size,
181and Β§12 carries the trigger for changing that). The sequencing is Β§11.
182