Shows Hindsight Graph as as Read Only. useful to make a display to show others
  • HTML 75.6%
  • Shell 22.7%
  • Dockerfile 1.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jriyuu 6acea8b7ae
All checks were successful
build / image (push) Successful in 34s
Upload files to "assets"
2026-10-19 16:03:16 +11:00
.forgejo/workflows Update .forgejo/workflows/build.yml 2026-10-18 19:42:31 +11:00
assets Upload files to "assets" 2026-10-19 16:03:16 +11:00
docker-entrypoint.d Upload files to "docker-entrypoint.d" 2026-10-18 23:23:41 +11:00
html Upload files to "html" 2026-10-19 00:40:37 +11:00
Dockerfile Upload files to "/" 2026-10-18 19:46:02 +11:00
README.md Upload files to "/" 2026-10-18 23:23:19 +11:00

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:

  1. the URL — ?view=3d, ?theme=galaxy, or both: http://<host>:8085/?view=3d&theme=galaxy#gideon
  2. the last choice made on that browser
  3. VIEW_MODE / THEME (defaults 2d / 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.