Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Gandalf Proxy

A reverse proxy for gandalf.lakera.ai that logs every student prompt and response. Students interact with Gandalf normally, the proxy sits invisibly in between, captures all API traffic, and stores it in a local SQLite database.

It works by injecting a small script into every Gandalf page that reroutes the game's API calls through the proxy, where each request is recorded before being forwarded to Lakera unchanged.

Configuration

Configuration is through environment variables, all optional.

  • PORT - port to listen on. Defaults to 3000.
  • PUBLIC_URL - external base URL the browser uses. Defaults to http://localhost:PORT. Set this if the proxy runs behind a domain or tunnel.
  • SESSION_SECRET - key for signing session cookies. If unset, a random secret is generated and persisted to data/.session-secret so sessions survive restarts.

Users are defined in data/roster.json, keyed by ID, each with a password and a role of student or admin.

{
  "admin":  { "password": "admin123", "role": "admin" },
  "STU001": { "password": "STU001",   "role": "student" }
}

If the file is absent, a small built-in roster is used. Passwords are currently stored in plain text.

Usage

Requires Node.js. Install and start:

npm install
npm start

The proxy is then available at PUBLIC_URL. Students sign in with their roster ID and password and are proxied to Gandalf. The admin account is redirected to /admin.

For development with auto-reload:

npm run dev

The database is created automatically at data/prompts.db on first run. Clear it from the admin panel before a real session.

Keeping it running

npm start stops when the terminal closes. To keep the proxy running in the background and restart it automatically, use a process manager such as PM2.

npm install -g pm2
pm2 start server.js --name gandalf-proxy

Useful commands:

pm2 logs gandalf-proxy      view output
pm2 restart gandalf-proxy   restart
pm2 stop gandalf-proxy      stop

To relaunch on machine reboot, run pm2 startup once and follow the printed instruction, then pm2 save. The server closes its database cleanly on stop, so PM2 restarts do not corrupt or leave stray database files.

Software

  • Node.js with Express
  • express-session for authentication
  • http-proxy-middleware for both proxies
  • better-sqlite3 for storage, in WAL mode
  • Vanilla HTML, CSS, and JavaScript for the admin panel, served statically. No build step.

Serverside structure

server.js              Entry point: config, database, routes, both proxies
package.json
data/
  roster.json          User accounts (IDs, passwords, roles)
  prompts.db           SQLite database (generated)
  .session-secret      Session key (generated, git-ignored)
public/
  admin/
    admin.css          Admin panel styles
    admin.js           Admin panel client logic

server.js renders the login and admin HTML and holds all server logic. The admin panel's CSS and JavaScript are served as static files from public/.

The database has two tables. entries holds the structured columns queried by the panel (student, timestamp, request type, level name and number, prompt, guess, answer, LLM, pass state). entries_raw holds the full raw request and response payloads, keyed by entry ID and read only by the raw export. Splitting the payloads out keeps the queried table small. Startup migrations bring older databases up to the current schema.

Workflow

[Browser]
  |
  |-- Loads a Gandalf page through the proxy
  |     Proxy injects the intercept script before serving the page
  |
  |-- Student sends a prompt or password guess
  |     Script reroutes the API call to /gandalf-api on the proxy
  |
[Proxy]
  |-- Buffers the request body
  |-- Forwards it to gandalf-api.lakera.ai unchanged
  |-- Reads the response
  |     send_message and guess_password: save an entry (structured + raw)
  |     get_defender and trial_assignment: cache level number and LLM
  |-- Returns the response to the browser unchanged
  |
[Browser] receives the normal game response

Level names map to numbers reported by Gandalf. Main game levels are 1 and up. Side challenges (Adventures) report 0 and share the same endpoints, so they are logged but excluded from the leaderboard and grades.

The admin panel at /admin has two tabs. The submission log is a filterable, paginated feed of all entries with live refresh, plus total, pass, fail, and student counts. The leaderboard ranks students by highest main game level reached, with prompts sent and last active time. Exports:

  • JSON - structured entries, respects filters
  • CSV - same as JSON in tabular form
  • Raw - structured entries plus full raw payloads
  • Grades - one row per student with their highest main game level, for grading

Open spots and further improvements

  • Passwords in roster.json are plain text and should be hashed.
  • The session cookie uses secure: false for local HTTP. Set it to true when serving over HTTPS.
  • All student traffic exits through the proxy's single IP, which may hit Lakera's rate limits at scale.
  • Agent Breaker mode is not captured. Its attack submissions never reach the proxy, so nothing is logged. Supporting it would require intercepting its separate endpoints.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages