- HTML 75.6%
- Shell 22.7%
- Dockerfile 1.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| assets | ||
| docker-entrypoint.d | ||
| html | ||
| Dockerfile | ||
| README.md | ||
hindsight-memory-graph
Read-only kiosk viewer for Hindsight memory graphs. One container, one tab per Hindsight, configured entirely with environment variables. Built for an always-on screen (e.g. a Pi in kiosk mode).
Two ports: 80 for your LAN (every tab) and 8080 for the public (only tabs you mark public, e.g. a demo). Put a reverse proxy in front of 8080 only.
nginx serves the page and one fixed graph query per tab. The browser can only ask for a tab id; which server, bank and limit that maps to is fixed at container start. Every other path and method returns 403, so no other Hindsight API (other banks, writes, deletes, reflect, admin) is reachable.
Run
docker run -d --name Memory-Graph --network ai-net -p 8085:80 -p 8086:8080 \
-e SOURCE_1_ID=gideon -e SOURCE_1_NAME=Gideon \
-e SOURCE_1_URL=http://Gideon-Hindsight:8888 -e SOURCE_1_BANK=gideon \
-e SOURCE_2_ID=tv -e SOURCE_2_NAME=TV \
-e SOURCE_2_URL=http://TV-Hindsight:8888 -e SOURCE_2_BANK=tv \
-e SOURCE_3_ID=demo -e SOURCE_3_NAME=Demo \
-e SOURCE_3_URL=http://Gideon-Hindsight:8888 -e SOURCE_3_BANK=demo \
-e SOURCE_3_PUBLIC=true \
<registry>/<owner>/hindsight-memory-graph:latest
The container must share a Docker network with every Hindsight it shows.
Open http://<host>:8085/. Add #<id> (e.g. /#tv) to open on, and stay on,
one tab — useful for a kiosk.
2D and 3D, Classic and Galaxy
The 2D / 3D dropdown in the header switches views live. All drawing happens in the browser, so the container does no extra work either way; the 3D library (~1.4 MB) only downloads the first time 3D is picked.
The Classic / Galaxy dropdown picks a theme. Galaxy is a black starfield with glowing memories; in 3D it adds a bloom glow and particles drifting along some links (an extra ~330 KB, also only loaded when used).
Which view and theme a screen opens with, first match wins:
- the URL —
?view=3d,?theme=galaxy, or both:http://<host>:8085/?view=3d&theme=galaxy#gideon - the last choice made on that browser
VIEW_MODE/THEME(defaults2d/classic)
Mouse and touch
| 2D | 3D | |
|---|---|---|
| drag | pan | rotate around the graph |
| scroll / pinch | zoom | fly in and out |
| right-drag | — | pan |
| hover a memory | shows its text | shows its text |
| click a memory | zooms to it | flies to it |
A clicked memory also shows in the bottom ticker. Touching the graph pauses the
auto-tour (2D drift, 3D orbit, tab rotation); it eases back
RESUME_SECONDS (default 30) after the last touch. Set INTERACTIVE=false for
a screen nobody touches.
3D uses WebGL and slowly orbits the camera. A Pi 4/5 handles a few hundred
memories in 3D; if it stutters, lower MAX_LINKS or SOURCE_N_LIMIT, or set
ROTATE_SPEED=0. Galaxy in 3D is the heaviest combination (the glow is a
full-screen effect); on a Pi 4, galaxy in 2D is the safer pick.
Tabs
One set per tab, N = 1–50 (gaps are fine, tab order follows N):
| Variable | Required | Example | Notes |
|---|---|---|---|
SOURCE_N_ID |
yes | gideon |
lowercase letters, digits, -; used in the URL |
SOURCE_N_URL |
yes | http://Gideon-Hindsight:8888 |
Hindsight API, scheme + host + port only |
SOURCE_N_BANK |
yes | gideon |
bank to show |
SOURCE_N_NAME |
no | Gideon |
tab label (defaults to the id) |
SOURCE_N_LIMIT |
no | 200 |
max memories fetched (defaults to DEFAULT_LIMIT, 200) |
SOURCE_N_PUBLIC |
no | true |
also show this tab on the public port (default false) |
Invalid values stop the container at start with a clear message in the log.
LAN and public ports
| Port | Shows | Use |
|---|---|---|
80 |
every tab | your LAN, kiosk screens. Don't reverse proxy it. |
8080 |
only SOURCE_N_PUBLIC=true tabs |
point your public reverse proxy here |
Private tabs don't exist on the public port at all: requesting them returns 404, and they aren't in its tab list, so a visitor can't reach them by guessing ids or editing the URL.
The public port caches each graph for PUBLIC_CACHE_SECONDS (default 60), so
any number of visitors cause at most one Hindsight request per tab per minute.
A demo tab can point at a dedicated bank (e.g. demo) on any of your
Hindsights. Only that bank's graph is exposed. Fill it with sample content
rather than real data: whatever is in the bank is readable by anyone.
Display settings (all optional)
| Variable | Default | Meaning |
|---|---|---|
VIEW_MODE |
2d | default view, 2d or 3d |
THEME |
classic | default theme, classic or galaxy |
ROTATE_SPEED |
0.6 | 3D: camera orbit in degrees per second (0 = still) |
INTERACTIVE |
true | mouse/touch: rotate, zoom, click a memory to fly to it |
RESUME_SECONDS |
30 | resume the auto-tour this long after the last touch |
REFRESH_MINUTES |
5 | re-fetch the shown graph |
ROTATE_MINUTES |
0 | auto-switch tabs (0 = off; off once a tab is clicked or #id is used) |
MAX_LINKS |
1500 | cap edges drawn — keep a Pi smooth |
LABEL_COUNT |
6 | label the N most-connected memories |
NEW_HIGHLIGHT_MINUTES |
30 | 2D: pulse memories added within this window |
TICKER_SECONDS |
9 | how long each memory shows in the bottom ticker |
TICKER_SIZE |
12 | how many of the newest memories the ticker cycles |
CAMERA_SECONDS |
25 | 2D: how often the view drifts to a new memory |
HIDE_CURSOR_SECONDS |
3 | hide the pointer when idle |
MERGE_DUPLICATES |
true | merge facts with identical text (raw fact + its observation) |
PUBLIC_CACHE_SECONDS |
60 | public port: how long a graph is cached |
Check it's locked down
curl -s -o /dev/null -w "%{http_code}\n" http://<host>:8085/api/graph/gideon # 200
curl -s -o /dev/null -w "%{http_code}\n" http://<host>:8085/api/graph/nope # 404
curl -s -o /dev/null -w "%{http_code}\n" http://<host>:8085/v1/default/banks # 403
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://<host>:8085/api/graph/gideon # 403
# public port: only public tabs
curl -s http://<host>:8086/sources.json # only public tabs listed
curl -s -o /dev/null -w "%{http_code}\n" http://<host>:8086/api/graph/demo # 200
curl -s -o /dev/null -w "%{http_code}\n" http://<host>:8086/api/graph/gideon # 404
Pi kiosk
mkdir -p ~/.config/autostart
cat > ~/.config/autostart/memory-graph.desktop <<'EOF'
[Desktop Entry]
Type=Application
Name=Memory Graph
Exec=chromium-browser --kiosk --noerrdialogs --disable-infobars --incognito --check-for-update-interval=31536000 http://<host>:8085/?view=2d#gideon
EOF
(Newer Pi OS images call the binary chromium. Change ?view=2d to ?view=3d
for the 3D view; --incognito forgets dropdown choices, so the URL decides.) The page reloads itself daily
and keeps the last graph on screen if a Hindsight is unreachable.
Build
The Forgejo workflow in .forgejo/workflows/build.yml builds linux/amd64 and
linux/arm64 and pushes to the instance's container registry on every push to
main (:latest, :sha-xxxxxxx) and on v* tags (:1.2.3). It needs a
repository secret REGISTRY_TOKEN (access token with package read/write).
Local build: docker build -t hindsight-memory-graph .
Privacy
Read-only stops changes, not reading: every tab shows what that agent knows. Keep port 80 on your LAN. Only port 8080 should go through a public reverse proxy, and only tabs whose bank you're happy for anyone to read should be marked public.
Credits
Graph rendering by force-graph and
3d-force-graph (MIT, includes
three.js, MIT), bundled so the viewer works offline. galaxy-fx.min.js is a
minified subset of three.js 0.186 (MIT) — the bloom pass,
sprites and star points used by the galaxy theme.