Two acceptance modes
Active click-consent — the user ticks a box against the exact, versioned consent text. Passive tacit acceptance — a new version counts as accepted unless objected to within an objection period.
Self-hosted · Apache-2.0
clickwrap is a self-hosted service for capturing provable acceptance of versioned agreements — terms of service, DPAs, privacy policies, any document your users must accept — backed by an append-only evidence chain. It also archives externally-signed documents as immutable per-customer evidence.
One compliant: true | false answer per customer. Two acceptance modes. A tamper-evident audit trail. Runs in a single container.
Active click-consent — the user ticks a box against the exact, versioned consent text. Passive tacit acceptance — a new version counts as accepted unless objected to within an objection period.
A passive objection period begins only on provable access (e-mail delivery confirmation or a confirmed popup display) — never on publish alone. Active versions carry an absolute hard deadline stamped at rollout.
Who accepted which version, when, from which IP / user-agent, against which exact consent text and document hash. Immutable — enforced by DB privileges; corrections are new rows, never edits.
Downstream apps ask one question — is this customer allowed in? — and get compliant: true | false with per-document detail. The single integration point for your portals and tools.
One document per (type × audience) with an immutable version history (DRAFT → PUBLISHED → RETIRED). Publish rolls out to customers, tracks deadlines, sends reminders and escalates.
Flag a document type external to skip the clickwrap machinery and instead store already-signed PDFs (e.g. counter-signed offers) per customer as immutable evidence — never part of the compliance gate.
E-mail delivery, file storage, admin auth, the acceptance-page renderer and admin notifications are all plugins, discovered from installed npm packages. Ship your own without forking.
A React admin UI for legal / operations staff: documents, versions, customers, compliance dashboards and the evidence history — served from the same container.
The combined image serves the API, the hosted acceptance page and the admin UI from one container. This runs a self-contained demo (in-memory store, no external services):
docker run -p 3000:3000 \
-e ADMIN_API_TOKEN="$(openssl rand -hex 24)" \
-e SERVICE_API_TOKEN="$(openssl rand -hex 24)" \
-e ACCEPTANCE_LINK_SECRET="$(openssl rand -hex 32)" \
-e PUBLIC_BASE_URL="http://localhost:3000" \
ghcr.io/jramm/clickwrap-combined:latest
Then open http://localhost:3000 — a bare / redirects to the admin UI at /ui. For a production setup (Postgres via Prisma, S3 storage, an e-mail provider), see the README and docs.
The API + hosted acceptance page only. Put your own frontend in front of it.
nginx serving the admin SPA, proxying /api to a separate backend.
Everything in one container — API at the root, admin UI under /ui.
Public images on ghcr.io/jramm/clickwrap, -admin-ui and -combined — anonymous pull, semver-tagged.
Everything pluggable ships as an npm package the host discovers at boot: e-mail providers (Postmark / SMTP / no-op), file storage (memory / local / S3), admin auth (Google SSO / static token / SuperTokens), the acceptance-page renderer and admin notifications. Build your own against the typed SDK:
npm i -D @jramm/clickwrap-plugin-sdk
See the plugin guide for the definePlugin contract and a worked example.
Open source, Apache-2.0, no SaaS lock-in. Your agreements and evidence stay on your infrastructure.