Producer
Write in Emacs
Native pmbah-mode for content-blind writing records from GNU Emacs.
pmbah-mode is a buffer-local minor mode for GNU Emacs 29.1+ that records the shape of your editing as a content-blind process record. When you choose to sign, it uploads only the public, content-blind manifest and event log to the configured ingest service and copies the returned record URL to your kill ring. Nothing about what you typed leaves your machine; only the shape of the editing does.
What it captures
- Buffer mutations recorded after
pmbah-modestarts, not raw keystrokes, OS-level input, or pre-existing buffer contents. - If the buffer is already non-empty, the mode still records only later mutation positions/lengths/timing. It does not store a starting buffer length, snapshot, hash, or replay fixture. Some length-derived stats may be
unknownbecause the verifier cannot infer total document length from the captured suffix alone. - Codepoint-anchored process metadata: insert, delete, and replace operations with zero-based Unicode codepoint offsets and lengths. Wall-clock timing relative to the session start.
- Source attribution where reliable. Common Emacs commands (
self-insert-command,yank,kill-region, and so on) map to typing / paste / cut / etc.; ambiguous cases fall back tounknownrather than guess.
What it does not capture
- Your document text. No plaintext leaves the producer. The local Node helper that builds the public record is passed numeric process metadata only, with one sanctioned exception: if you choose to bind the document at sign time, the helper receives the selected text transiently so it can compute the content-blind binding commitment, then discards it. Only the commitment is uploaded; the text never leaves your machine. See Bind and check a document.
- Absolute local file paths. The capture-context review preview shows the path as omitted by default.
- Anything outside the buffer
pmbah-modeis attached to. The mode is per-buffer.
Requirements
- GNU Emacs 29.1 or newer.
- Node.js available to Emacs. GUI Emacs on macOS / Linux often does not inherit your shell
PATH; you may need to point Emacs at an absolute Node path (see Configuration below). - A checkout or release directory containing both
pmbah-mode.elandscripts/build-record.mjs. - Repository dependencies installed from the repo root with
npm ci(ormake install). - A running ingest service, e.g.
make local-containerfor local development.
The Emacs package is not on MELPA / ELPA for v0. Install from a checkout or release archive.
Install from a checkout
From the repository root:
git clone https://github.com/juanre/possiblymadebyahuman.git
cd possiblymadebyahuman
npm ci
# or: make install
Add one checkout root variable to your Emacs configuration and derive the producer paths from it:
(defvar pmbah-checkout-root
(expand-file-name "~/src/possiblymadebyahuman/"))
(add-to-list 'load-path
(expand-file-name "producers/emacs" pmbah-checkout-root))
(require 'pmbah-mode)
(setq pmbah-helper-script
(expand-file-name "producers/emacs/scripts/build-record.mjs"
pmbah-checkout-root))
;; Public service; this is also the package default.
(setq pmbah-api-base-url "https://possiblymadebyahuman.com")
Change only pmbah-checkout-root for your checkout location.
use-package users can use the same root variable:
(defvar pmbah-checkout-root
(expand-file-name "~/src/possiblymadebyahuman/"))
(add-to-list 'load-path
(expand-file-name "producers/emacs" pmbah-checkout-root))
(use-package pmbah-mode
:commands (pmbah-mode pmbah-sign-buffer pmbah-show-session-status)
:custom
(pmbah-api-base-url "https://possiblymadebyahuman.com")
(pmbah-helper-script
(expand-file-name "producers/emacs/scripts/build-record.mjs"
pmbah-checkout-root)))
Emacs 29’s package-vc-install can fetch the Lisp code but does not install npm dependencies for the Node helper. For v0, use a manual checkout / release directory and run npm ci there.
Configuration
API base URL
pmbah-api-base-url defaults to the public service:
(setq pmbah-api-base-url "https://possiblymadebyahuman.com")
You normally do not need to set it. If you previously copied local-development configuration such as (setq pmbah-api-base-url "http://localhost:8000"), remove that line or replace it with the HTTPS production URL above.
For local development, override the URL to match your local container:
PMBAH_PORT=18800 make local-container
export PMBAH_API_BASE_URL=http://localhost:18800
For the default local port:
(setq pmbah-api-base-url "http://localhost:8000")
Node path for GUI Emacs
If GUI Emacs cannot find Node, set either:
export PMBAH_NODE=/opt/homebrew/bin/node
or:
(setq pmbah-node-command "/opt/homebrew/bin/node")
Use the path printed by command -v node in a shell where Node is available.
Usage
- Open a writing buffer. It may already contain text; PMBAH records only later mutation metadata.
- Enable capture:
M-x pmbah-mode. The mode line showsPMBAH:N, whereNis the local event count. - Write normally.
- Check status when desired:
M-x pmbah-show-session-status. - Freeze, review the capture context, upload, and copy the record URL:
M-x pmbah-sign-buffer. - If you want to throw away the local session without uploading:
M-x pmbah-discard-session.
After a successful upload, the local event log is cleared and a fresh session starts for the current buffer. If upload fails, the local event log is retained so you can retry.
Verify the installation
A quick public-service check:
- In Emacs, open a buffer and run
M-x pmbah-mode. - Type a short draft.
- Run
M-x pmbah-show-session-status; confirm the API URL ishttps://possiblymadebyahuman.com. - Run
M-x pmbah-sign-buffer; review the capture context preview, upload, and confirm a short URL is copied to the kill ring.
For a local development check instead, start with make local-container (or PMBAH_PORT=18800 make local-container) and set PMBAH_API_BASE_URL / pmbah-api-base-url to the matching local origin.
Capture context review
pmbah-sign-buffer opens a *PMBAH capture context* preview before upload. It shows:
- the buffer name candidate;
- the major mode candidate;
- absolute file path status (omitted by default);
- the content-blind upload guarantee.
It then asks separately whether to include emacs.buffer_name and emacs.major_mode. If both are declined, the context is only:
{ "surface": "emacs" }
Event semantics
- Emacs supplies
after-change-functionsarguments(beg end len)in character positions.pmbah-moderecords zero-based Unicode codepoint offsets and lengths. insert,delete, andreplaceare derived from the Emacs mutation.- Source attribution: the mode identifies a few common commands (
self-insert-command,yank,kill-region, and so on) and falls back tounknownwhen attribution is uncertain. It declares thetimingandpause_fidelitycapabilities; it does not claimsource_attributionorkeystroke_level. - The mode can start in a non-empty buffer. It records absolute positions and lengths for later mutations only. It does not upload a starting length, text, a text hash, or a replay fixture; length-derived stats may be unknown when capture starts after existing content.
Troubleshooting
PMBAH helper script is not readable: setpmbah-helper-scriptto the helper path in the checkout where you rannpm ci/make install.Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@noble/hashes': install repository dependencies from the repo root (npm ciormake install) and confirmpmbah-helper-scriptpoints toproducers/emacs/scripts/build-record.mjsinside that same checkout.Searching for program: No such file or directory, node: GUI Emacs cannot find Node. SetPMBAH_NODEorpmbah-node-commandto an absolute Node path.generated record failed verification: keep the local session and report the sequence; the helper rejected an internally inconsistent public process record before upload.- Upload HTTP errors: run
M-x pmbah-show-session-statusand confirmpmbah-api-base-urlishttps://possiblymadebyahuman.comfor normal public use.http://localhost:8000only works when you are runningmake local-containerlocally. The API origin must servePOST /api/records, and/readyshould be healthy. - No URL copied: upload did not complete; the local session is retained for retry.
Sibling producers
The browser writing page is the no-install producer: an empty drafting canvas in your browser that records edits made inside it, signs, and returns a short URL. A capture-all browser extension producer of the same record format is also in the repository (apps/browser-extension/); its public install path will be linked here once the Chrome Web Store listing is approved.
All three producers (Emacs, the browser writing page, and the extension) sign content-blind manifests that packages/format verifies the same way. See the verification page for the chain of trust and the records page for the public record format.