Wampum: Mud, Reeds, and 16 GB

A Mac mini with 16 GB, local Qwen, and a cybersecurity workbench that keeps the scenario, browser actions, and evidence together. An MIT source prototype with the failed checks left in the record.

I got tired of negotiating with a settings file.

Qwen could answer. The browser could open. Getting the model, the editor, the right page, the saved work, and the next conversation to agree on where we were took considerably more diplomacy.

I had a Mac mini M4 with 16 GB of unified memory. I was keeping the computer. The software could move.

So I started building Wampum, a personal cybersecurity workbench for local red-team research and prototypes. Native SwiftUI and AppKit. Ollama underneath. A place to edit a file, keep the scenario straight, inspect the browser, review a proposed action, and retain evidence when one part of the operation fails.

I grew up finding wampum in the mud and reeds near Kennedy Airport. The name had been sitting around longer than the application. The icon has the marshes, the airport, and jets overhead. The first version also had a kid with a hand that needed its own investigation. We removed the kid.

Wampum marshes and jets icon

The computer stays

The development machine runs macOS 26.6.2 and Apple Swift 6.4. Wampum declares macOS 14 as its deployment target. I have tested this machine, not every Mac that can run that target.

The installed model is lukey03/Qwen3.5-9B-abliterated:latest, a 9B Q4_K_M model served by Ollama 0.33.2. Its inspected capabilities include completion, tools, and thinking. Vision is absent.

That detail changes the application. Wampum can keep local image copies and extract text with optical character recognition. It does not send image pixels to this model and pretend that a setting gave it eyes.

The starting context profile is 8K, with 4K, 16K, and 32K options. The input estimate is approximate. Older complete turns can leave the current request while the full transcript remains on disk. Scope, notes, rejected tools, and the next step stay with their scenario. A fresh chat should not send the operator back to archaeological work.

There is no Wampum account, analytics uploader, or hosted inference fallback. Its model client accepts loopback connections and refuses redirects. Browser tools can still contact websites. An approved external command can still transmit data. Keeping inference local does not make every other process local by association.

A click can survive a failed screenshot

This one earned code.

A browser action can succeed, then fail to produce its next snapshot. If the application reports the whole operation as failed, the obvious response is to click again.

That is a poor recovery plan for a payment, a submission, or a destructive button.

Wampum saves intent before sending the action. It then records execution, page refresh, and screenshot capture separately. A tool-reported success survives a failed observation. A lost transport reply becomes unknown. Restart does not replay the action to find out.

The browser workspace shows the controlled page through a screenshot, alongside the document snapshot, proposed call, console output, and diagnostics. The model proposes one action. I review the arguments. Allow sends that call. Deny stays in scenario memory, so the model does not get another attempt merely by changing the wording.

The current browser session is an isolated session Wampum owns. Attaching every existing signed-in browser profile is separate work.

Stop the children, too

The original check runner signaled its shell. That did not establish that its children had stopped.

Checks now launch into an owned process group. Stop sends TERM, then KILL after a short grace period. Normal completion cleans remaining group members before the leader is reaped. Tests create children and grandchildren that ignore TERM, then check that the group disappears while an unrelated process keeps running.

A deliberately detached process can escape that group. Force quit or a machine failure can also leave work alive. On restart, the application preserves the last check's output, warns that ownership was interrupted, and does not launch the command again or kill a saved process ID that might now belong to something else.

The editor still has an unsaved draft to protect while all this happens. That is part of the test.

Ten seconds loading, then a short reply

One recovery check timed out. I kept the failed result.

Later, with no loaded models reported, a short READY request took 10.56 seconds. Ollama reported about 9.98 seconds of loading. That gave me a measured cold start. It did not explain the earlier timeout by itself.

The revised regression first requires a successful warmup. It then cancels a longer synthetic request and asks for READY again.

WAMPUM_LIVE_TEST=1 swift test --filter LocalOllamaTests

One warmed run returned from cancellation in 0.00464 seconds. The next READY response took 0.3776 seconds and returned two output tokens. The default suite separately checked active local HTTP cancellation, connection closure, saved history, and the next reviewed request.

Those are useful observations about a short request on this machine. They do not measure the instant GPU computation stops, maximum throughput, or an afternoon with every application open.

A page cannot approve its own instructions

The approval record now binds the exact call hash to its scenario before the request leaves. Page context and the reviewed Qwen request preview have separate hashes. Current scope, notes, and goal have hashes, too. Raw source text does not get copied into that metadata.

The companion monitor checks the call binding, an independently supplied scenario, approval ordering, and reuse of an approval ID. A page remains untrusted source evidence even when it informed a proposal that I approved. The report keeps the authority link separate from that source history.

These records are local and unsigned. Someone controlling the same account can rewrite local files. An approval also cannot establish that the decision was sensible or that an engagement allowed it. The tool has to retain those distinctions without making the screen impossible to use.

The installed Qwen model passed a local browser check through this path: proposed click, operator approval, changed page marker, and matching console evidence. The target was an owned loopback fixture. No client result gets borrowed to decorate it.

A baseline before a verdict

I also wanted to check what changed after a model had been used with browser tools.

The Integrity workspace hashes the selected Ollama manifest and its referenced blobs, then compares selected saved context against a preserved baseline. The first run covered the manifest, four blobs, and 14 context files. Declared blob digests matched. An immediate comparison found zero changes across those 19 files.

A first baseline cannot tell me whether the downloaded model was trustworthy to begin with. It gives me something to compare next time. Unreadable evidence produces an incomplete result. Legitimate note edits produce changes. Neither result needs a theatrical red light labeled POISONED.

I tested the native interface in a separate app instance with temporary state. After adding one synthetic context file, it displayed the added path and hash. The original baseline stayed untouched, and the real work session stayed open.

The prototype record

The first foundation commit is timestamped October 25, 2026, at 9:23 p.m. New York time. Subsequent commits record the network boundary, scenario memory, browser, controller interface, receipts, process cleanup, integrity panel, and approval links. Commit timestamps show checkpoints. They do not account for every minute between them.

The public release checkout executed 93 tests: 86 passed and seven were explicitly skipped in the default run. With the local model, browser, and controller checks enabled, 90 passed and three were skipped. These counts describe the same suite under different conditions; they do not add together. The browser targets were local fixtures. The companion monitor passed its 30 original assertions and 11 replay tests.

The repository's build record keeps the failures alongside the results. One integration test assumed a Homebrew Node path that did not exist. The fix used the runtime the application actually detected. Small assumption, real failure, recorded correction.

Native testing caught another one after the first source cut: temporary-directory aliases confused the file explorer. The tests had passed while the interface listed broken relative paths. I fixed the path handling, added regressions, rebuilt, and opened the files in a separate app instance. Then the reviewed Qwen request returned READY and saved locally. Version 0.1.1 includes that correction.

Wampum native editor and local Qwen response, captured during release testing

Codex helped write the implementation and run the checks. I supplied the workflow, chose the requirements, and reviewed what actually happened. The source is there for anyone who wants to inspect that division of labor.

The build commands remain short:

swift test
scripts/build-app.sh

The MIT source and 0.1.1 prototype release are public. The release evidence records the checked environment and limits. This is a source release; there is no notarized installer. I am testing the prototype as I build it. Streaming responses, editor tabs, search, richer diffs, an interactive terminal, browser-process cleanup, and longer sessions still have work attached to them.

For now, I can see the proposed call, keep the result when the screenshot fails, and carry the scenario into the next conversation. That is enough machinery to keep building without losing the thread.

Before anyone asks what I’m on: Caffè Kimbo. A personal thank-you to Kimbo for keeping the night shift civilized.

GhostInThePrompt.com // A failed screenshot does not undo the click.