# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Safsaf is a web framework for Guile Scheme, built on Guile Fibers using the Guile Knots web server. ## Environment Setup The project uses `direnv` with Guix. The `.envrc` runs `use guix -D -f guix-dev.scm`, which pulls in all dependencies: `guile-knots`, `guile-webutils`, `guile-lib`, `guile-json-4`, `guile-squee`, `guile-sqlite3`, `guile-gcrypt`. Run `direnv allow` to activate the environment. All Guile dependencies are on `GUILE_LOAD_PATH` via the Guix profile. ## Key Dependencies ### Guile Knots - `(knots web-server)` — `run-knots-web-server`: the HTTP server. Handler signature is `(request body-port) → (values response body)`. `body-port` is the port for reading the request body lazily. - `(knots resource-pool)` — `with-resource-from-pool`: DB/resource connection pooling. - `(knots parallelism)` — `fibers-let`, `fibers-parallel`, `fibers-map`: concurrent work in handlers. - `(knots thread-pool)` — `call-with-thread`: offload blocking/CPU-bound work. - `(knots timeout)` — `with-fibers-timeout`, `with-port-timeouts`: request and I/O timeouts. - `(knots web)` — `call-with-connection-cache`: outbound HTTP with connection pooling. - `(knots)` — `call-with-sigint`, `format/knots`, `spawn-fiber/knots`. - `(knots web-server)` also exports `make-chunked-output-port/knots`, `sanitize-response`, `request-body-port/knots`, `read-request-body/knots`. ### Guile Webutils - `(webutils multipart)` — `parse-request-body`, `` record, `parts-ref`, `parts-ref-string`. - `(webutils cookie)` — `set-cookie`, `delete-cookie`. Registers `Cookie`/`Set-Cookie` header parsers with `(web http)`. - `(webutils sessions)` — ``, HMAC-signed cookie sessions. Format: `signature$expires$base64-data`. - `(webutils date)` — RFC3339 and HTTP date conversions. ### Guile JSON (v4.7.3) - `(json)` — Re-exports everything from parser, builder, and record modules. - `(json parser)` — `json->scm` (from port), `json-string->scm` (from string). Options: `#:null` (default `'null`), `#:ordered` (preserve key order). - `(json builder)` — `scm->json` (to port), `scm->json-string` (to string). Options: `#:pretty`, `#:unicode`, `#:validate`. - `(json record)` — `define-json-mapping`: bidirectional SRFI-9 record ↔ JSON conversion. - Data mapping: objects ↔ alists, arrays ↔ vectors, strings ↔ strings, numbers ↔ numbers, `true`/`false` ↔ `#t`/`#f`, `null` ↔ `'null`. ### Guile Gcrypt - `(gcrypt random)` — cryptographic random bytes (used for CSRF token generation). ### Guile Lib - `(htmlprag)` — HTML/SHTML parsing and generation. `html->shtml`: parse HTML to SXML. `shtml->html`: render SXML to HTML string. `write-shtml-as-html`: write SXML to port. - `(logging logger)` — Logging framework. `(logging port-log)` — log to ports. `(logging rotating-log)` — rotating file logs. - `(md5)` — MD5 hashing. - `(container async-queue)` — `make-async-queue`, `async-enqueue!`, `async-dequeue!`. - `(string transform)`, `(string wrap)`, `(string completion)` — String utilities. ### Guile Standard Library - `(web request)`, `(web response)`, `(web uri)`, `(web http)` — Guile's built-in HTTP types. - `(srfi srfi-9)` — Record types. `(srfi srfi-64)` — Test framework. `(srfi srfi-71)` — Extended `let` with multiple values; prefer over `(srfi srfi-11)` `let-values`. ## Architecture Handler signature throughout is `(request body-port) → (values response body)`, using Guile's `` directly. `body-port` is the port for reading the request body lazily. Context is threaded via Guile parameters, not a wrapper record. Safsaf wraps `run-knots-web-server` with: 1. **Parameters for context** — `current-route-params` (alist of matched route bindings), `current-reverse-routes` (for `path-for`). Handler wrappers add their own parameters (e.g. `current-csrf-token`, `current-session`). 2. **Router** — data-driven route table using `(route method pattern handler)`. Patterns are lists of segments: strings (literal match), symbols (capture), or `(predicate name)` pairs. Dotted-tail patterns (e.g. `'("api" . rest)`) capture remaining segments. Routes can be organized with `(route-group prefix ...)`. Named routes support reverse routing via `path-for`. 3. **Handler wrappers** — convention: `(foo-handler-wrapper handler) → handler'`. A handler wrapper transforms the request on the way in and the response/body on the way out. Wrappers that need configuration provide a `(make-foo-handler-wrapper ...)` constructor. Applied to route trees via `wrap-routes`, which accepts one or more wrappers. 4. **Entry point** — `(run-safsaf routes #:key host port method-not-allowed? method-not-allowed-handler connection-buffer-size)` compiles the route table, builds the dispatch handler, and starts the HTTP server via `run-knots-web-server`. When called outside a Fibers scheduler, it wraps everything in `run-fibers` and blocks until Ctrl-C. When called inside an existing scheduler (e.g. within `run-fibers`), it just starts the server and returns immediately. `method-not-allowed?` defaults to `#t`, enabling automatic 405 responses. Handler wrappers are applied to routes via `wrap-routes` before passing to `run-safsaf`. ## Finding Guile Library Sources To read source code for Guile dependencies, look them up via `GUILE_LOAD_PATH`. The first entry is the project's Guix profile directory containing all dependencies. Do **not** search `/gnu/store` directly — it is slow and noisy. A Guile module path like `(knots web-server)` maps to the file `knots/web-server.scm` under a load path directory. To find it: ``` ls "$GUILE_LOAD_PATH" | head # see what's available cat "$(echo $GUILE_LOAD_PATH | cut -d: -f1)/knots/web-server.scm" # read a specific module ``` Or use the Read/Glob tools directly against the first `GUILE_LOAD_PATH` entry (e.g. `/gnu/store/...-profile/share/guile/site/3.0/`). Module path segments map to directories, with the final segment as `.scm`. For example: - `(json parser)` → `json/parser.scm` - `(webutils multipart)` → `webutils/multipart.scm` - `(srfi srfi-9)` → `srfi/srfi-9.scm` ## Guile Conventions - Predicates end with `?`. Setters use `set-` prefix. Constructors use `make-`. - Records defined with `define-record-type` from `(srfi srfi-9)`. - Modules use `define-module` with `#:use-module` and `#:export`. - Use `values` for multiple return values, `call-with-values` or `receive` to consume them.