Pumasi Sign
An unmetered, legally-binding B2B e-signature platform. Deterministic PDF stamping, SHA-256 tamper-evident audit certificates, public multi-tenant signing. Alpha; no licence file yet.
- Maturity
- alpha
- Instead of
- DocuSign, SignWell
- Source
- GitHub
Before you use it: two defects are live on sign.pumasi.ai as this is written, both repaired in main and neither deployed. Re-measured on the live hosts and the deployed tree 2026-09-01 05:20:32–05:22:52 UTC, at this seat's own clock rather than carried from an earlier one; the served root still loads /assets/index-CnoFAC2c.js, the fingerprint STAGE.md §2.2 maps to the deployed tree 0e26917, and wrangler.jsonc ships the worker and frontend/dist as one artefact. (1) The correct expiration & reminders pencil silently deletes the covering note you wrote to your signers. The served EnvelopeDetailView-C4VlFBtA.js PATCHes exactly three fields — expires_at, reminders_enabled, reminder_interval_days — never message, then reports Envelope settings updated.; at 0e26917 durable.ts:1211 writes NULL to message on any body that omits it. Messages already deleted are gone — the release note says so in the sender's own words, and the deployed tree stores no prior value to read back: the UPDATE overwrites the column and the corrected audit row at durable.ts:1214 carries no details_json. Repaired at 9659e69, release note — Q-037, and the window that entry's own row records closes 2026-09-08. (2) An expiration date does nothing. The served ExternalSignView-CmDSqiPb.js carries four closed-envelope cases and no expired one, and at 0e26917 there is no crons key, no scheduled export and no expires_at write on that route — so nothing expires, and the only refusal is client-side, in SignView-BM1dkPaZ.js, beside a source comment (SignView.vue:480 at 0e26917) claiming the server rejects /complete the same way when at that commit it does not. Repaired at 2471a29, release note — Q-035, and the window that entry's own row records closes 2026-09-08. Both are merged and undeployed, and this page does not claim either has shipped: nobody owns deployment — pumasi/DECISIONS.md Q-012, open, and outside CHARTER Part 0's proceed-on-default rule. Separately, this product's own front page went live on 2026-09-01 and it tells a stranger, three times, that the code is under Apache-2.0 — and the repository grants no licence at all. Measured on the served landing chunk /assets/LandingView-C5khdw3s.js (200, 10 046 bytes, re-fetched 2026-09-01 05:22:14 UTC): Apache-2.0 appears in the hero strip, in the stage banner, and in a comparison-table row headed License & Source Code whose neighbouring columns both read Proprietary Closed Source — a row that invites a reader to choose this product because of terms that do not exist. Against that, gh api repos/pumasi-ai/pumasi-sign/contents/LICENSE returned 404 at 2026-09-01 05:22:52 UTC — the contents API, which is the check that answers whether the file exists, rather than the licenseInfo field, which has been wrong about this organisation before, and a public repository with no licence grants no rights at all: you may read the code, and you have no permission to run, modify or redistribute it. Which way that resolves is the steward's, open as pumasi/DECISIONS.md Q-021, and this page states no licence until it lands — it reports the deployed page's claim rather than repeating it. The entry-path defect this card warned about for two days is gone. Issue #7 — sign in again handed the user a page of raw JSON — was closed 2026-09-01T00:59:12Z, and the repair reached users in the deploy at 2026-09-01 01:02:16 UTC; verified on the live host rather than on the tracker, the button now builds /login?next=%2F, which answers 200 text/html. And the audit certificates verify document integrity, signer identity and UTC timestamps under the ESIGN Act and eIDAS, but Qualified Electronic Signatures (QES) requiring national hardware smartcards are not included.
Where this actually is#
alpha — and this page still has to tell you where that number comes from,
because a maturity claim with no source is exactly what this site exists not to
publish.
It comes from the product's own register:
roadmap/STAGE.md,
first published 2026-08-31 at
6e02cc4, which
records alpha set on measured evidence — not on aspiration, and not on a
chip. Read here at
f0d1912, which is
what origin/main answered when this card fetched it on 2026-09-01 — its
ladder, with the status that file's own exit-gate section gives each rung:
| Stage | Criteria | Status |
|---|---|---|
| 0 · Candidate | Steward selection | COMPLETE — built and deployed |
| 1 · Alpha | Pure-core suite passes 100%; both public landing surfaces live | NOT MET — both surfaces are live as of 2026-09-01; the gate is held on whether Surface B is honestly calibrated, and one of its claims is untrue |
| 2 · Beta | Real end-to-end users complete workflows without engineer intervention | PENDING |
| 3 · Launched | Production hardening, cross-model regression, 7-day veto window | PENDING |
Read that row 1 carefully, because it is the unusual part: the stage is
alpha, alpha's own exit gate is NOT MET, and on 2026-09-01 the reason
changed completely while the two words stayed the same. Surface A is this
page. Surface B — the product's own root landing page at sign.pumasi.ai — is
live, deployed at 2026-09-01 01:02:16 UTC. Issue
#8, which tracked its
absence, was closed three minutes before that at 2026-09-01T00:59:11Z — the
tracker moved first and the deploy is what made it true. Through five
evaluations the unmet half of this gate was Surface B is not live. It is now
Surface B is live and one of its claims is untrue — the licence claim, set
out below. On the gate's literal words the
row now reads met; the register declines to record it as met, because Stage 1
asks that second surface for honest calibration and a surface is not honestly
calibrated while one of its claims is a licence grant a stranger may act on.
What flips this row is a steward answering Q-021, not a build — and not
this page.
A correction to what this card said yesterday, because the file it pointed you
at has moved. Through 3edd06f this card disclosed that STAGE.md's gate
table still carried its pre-deploy wording — Surface B "has never been
deployed" — while that same file's exit-gate section, its §2.2 and its §5 all
recorded the deployment, dated it to the second, and quoted the live route
table, and it closed "The row is that file's to correct, not this one's."
That row is corrected, at
3f51fa1, with two
non-builder approvals recorded at
f0d1912 — verified
by this card against a freshly fetched origin/main on 2026-09-01. The Stage 1
row now opens "IN PROGRESS — NOT MET, and the unmet half is Surface B's
honesty, not its existence" and says in its own words which pass corrected
it. So STAGE.md no longer disagrees with itself, and if you open it looking
for the contradiction this card used to name, you will not find one. The
verdict did not move: the gate is NOT MET for the reason the row above gives,
and the correction was to the row's wording, not to the gate.
An earlier version of this page said seed, and said in bold that
pumasi-sign had no roadmap/STAGE.md. Both were true on the day it was
written and neither is true now. That repository carries STAGE.md, VALUE.md
and MARKET.md as of 2026-08-31, and this card is re-sourced from the first of
them — which is what STAGE.md §5 asks for by name.
Two disagreements you will meet, named rather than smoothed over.
The commons index still says
seed, and disagrees with itself about what to call the field.pumasi/catalog.jsonrecords this product at"status": "seed"in both its arrays — one rung below what its own register now says, andseedis not a rung on the product-manager role file's ladder at all. Re-read for this card atpumasi@2ab3a4fon 2026-09-01 and unchanged, down to a top-levelupdatedof2026-08-29— three days stale, across the tick in which this product deployed its front page. Re-read again at23bbc64later the same day: the only edit since is agovernance.product_rulespointer toPRODUCT-RULES.md; every product field and theupdatedstamp are as they were. The file is not merely stale but internally inconsistent:products[]keys both products on"status", while initems[]pumasi-signuses"status"andpumasi-bookinguses"maturity"— adjacent entries, same concept, two key names, both readingseed. That is why two readers can report different things about this file and neither be wrong. It has not been corrected because no role file owns it — open aspumasi/DECISIONS.mdQ-019, whose named default is a role-file amendment nobody has made — and this page may not edit it. Whoever inherits it inherits a schema question as well as a content one.The
BETAchip this card and the register argued about for two days is settled, and the deploy is what settled it. The chip was real —v-chip … BETAatfrontend/src/views/LandingView.vue:34as ofd797c81— and it was replaced by a badge derived fromfrontend/src/stage.ts, under a comment namingroadmap/STAGE.mdas the register — the same file this card reads. That is the version that went live. Read out of the served landing chunk on 2026-09-01 at 02:40 UTC, the constant and the badge it generates are there in one expression:var g=`alpha`,_=g.charAt(0).toUpperCase()+g.slice(1),v=`${g.toUpperCase()} — ACTIVE DEVELOPMENT`grep -c BETAon that chunk returns0. So the badge a stranger reads isALPHA — ACTIVE DEVELOPMENT, generated from the rung the register sets, and no user has ever seen aBETAbadge on this product — the argument was about a page nobody could yet open.
This page spent two days arguing about that chip. The argument was worth more than the answer, because of the vocabulary it forced into existence.
STAGE.md §5 keeps a four-state ledger for every claim this project makes.
A claim can be wrong in source and in production; right in both;
fixed in source and still wrong in production — the state that makes a
repository read as finished while a user keeps meeting the old behaviour, and
the direct, named cost of
pumasi/DECISIONS.md
Q-012, who deploys a merged fix, and by when, still open with its default
unclaimed; or merged and never shipped — present on main and absent from
every deployment there has ever been. That fourth state was added because of
this product's landing page, and this card's own bundle readings are cited in
that file as the measurement that forced the row.
On 2026-09-01 the page shipped, and the ledger moved in one motion. Until
that morning every sentence in LandingView.vue sat in the fourth state:
unshipped, and therefore harmless. The 01:02:16 UTC deploy took all of them
public at once — service/wrangler.jsonc serves frontend/dist as a single
ASSETS directory, so there is no shipping one claim and holding another. The
page had carried three claims the repository could not back. Two were
repaired before it went out: the BETA chip, which shipped reading ALPHA,
and an uncited competitor-pricing block, which shipped cited and dated, linking
both vendors' own pages. The third shipped unrepaired, and it is the
licence claim — which moved from merged and never shipped straight to
wrong in source and in production, the worst square on the board.
That difference is the whole reason this site keeps the vocabulary. An unshipped false claim is a bug report. The same claim, deployed, is something a stranger can act on — and the deployed one here is a grant of rights, in a table row whose neighbouring columns say Proprietary Closed Source, on a repository that grants none. The two could not be separated at deploy time, which is what Q-028 was raised to record; it did not get answered so much as overtaken, resolving itself by shipping.
The one thing the deploy made unambiguously better is worth naming, because
everything else it did was move a claim from private to public: the repository
can no longer disagree with the register about the rung. The badge is generated
from STAGE.md, and a frozen case in that repository fails the build if the
two drift apart. The stage word on the live page is right today by construction
rather than by luck — which is the only kind of right that survives the next
deploy nobody announces.
Where a file disagrees with STAGE.md, this page follows STAGE.md — not
because it is higher or lower, but because it is the one that shows its
evidence and names its falsifiers. Where STAGE.md disagreed with itself,
as its gate table did until 3f51fa1, this page followed the half that showed
the measurement and said which half that was — and that rule is recorded here
because it was used, not because it is needed today. The two halves agree
now.
Two defects are live on the hosted service, and both repairs are waiting#
If you are about to send an agreement through sign.pumasi.ai, read this
before the licence section below. Two release notes were published on
2026-09-01 eighty-three minutes apart — pumasi 14d8af4 at 01:41:04 UTC
and df8f0d0 at 03:04:29 UTC, read from the commit timestamps — and both
describe defects that are still live for users. Neither is a report about a page nobody can open: the surfaces are
deployed and reachable, and one of them silently destroys something the sender
wrote.
The deployment has not moved, and this card measured that rather than assumed
it. At 2026-09-01 05:20:32 UTC the served root still loads
/assets/index-CnoFAC2c.js — the same chunk fingerprint
STAGE.md §2.2
used to identify the deployed tree as
0e26917 — and
service/wrangler.jsonc binds ../frontend/dist as the worker's own ASSETS
directory, so the two ship as one artefact and an unmoved front end is an
unmoved worker. GET /api/health answered 200 at 05:20:33 UTC, naming
itself pumasi-sign. The product is
up, running the old code.
Re-measured by a second seat at 2026-09-01 05:50:00 UTC, before this page was
published, so none of it rests on an earlier seat's word. The served root still
loads /assets/index-CnoFAC2c.js; /api/health still answers 200; and each
chunk this section reads out is still the byte-for-byte artefact named below —
ExternalSignView-CmDSqiPb.js 5 250 bytes, SignView-BM1dkPaZ.js 46 980,
LandingView-C5khdw3s.js 10 046. The deployment has not moved.
1 · The settings pencil deletes the message you wrote to your signers#
What a sender loses. The send form gives you a box for a covering note to
the people signing — Please sign by Friday., This replaces the draft I sent
Tuesday. — and durable.ts returns it to every recipient on the token view, so
it is often the only explanation they get for what they are being asked to sign.
Elsewhere on the same screen there is a pencil for the expiration date and the
reminder settings. Opening it, changing nothing, and pressing save deletes the
note. The dialog then reports Envelope settings updated. — that string is in
the served chunk, beside the request that causes the loss — and closes. Nothing
warns, nothing asks, and nothing on the screen afterwards says the note is gone.
Both halves measured on the live host, 2026-09-01 05:22:14 UTC. The client half is in the chunk a sender's browser actually runs — and it is worth seeing the two dialogs side by side, because they call the same route:
$ curl -s https://sign.pumasi.ai/assets/EnvelopeDetailView-C4VlFBtA.js \
| grep -o 'patch(`/submissions/\${e}`,{[^}]*}'
patch(`/submissions/${e}`,{title:t,message:n||null}
patch(`/submissions/${e}`,{expires_at:t,reminders_enabled:U.value,reminder_interval_days:dt.value}
The first is the correct details dialog, which is about the message. The
second is the settings pencil, which is not — three fields, no message. The
server half is the deployed commit's own source, read at 0e26917 rather than
at main:
// service/src/durable.ts:1209-1211 at 0e26917 — the tree serving sign.pumasi.ai
`UPDATE submissions SET title = ?, message = ?, updated_at = ? WHERE id = ?`,
String(body.title ?? sub.title).slice(0, 200),
body.message != null ? String(body.message).slice(0, 2000) : null,
The title has a rule for the absent case. The message has none, so a request that never mentions it is treated as a request to erase it.
Merged, and not deployed.
9659e69 is in
main and tells absent (keep) from present-and-null (clear) — durable.ts
now reads body.message !== undefined ? … : (sub.message ?? null) at line 1326.
This page does not claim that repair has shipped, and neither does its own
note:
Pumasi Sign stops deleting the message you wrote to your signers,
a can-hurt release whose 7-day veto window closes 2026-09-08.
That date is DECISIONS.md Q-037's
own, reported here and not set here — this card sets no deadline on any
steward entry and has no standing to.
One thing the repair cannot do, and it is why this belongs on the card rather
than in a changelog: messages already deleted are gone. That is the release
note's own statement, in the sender's words, and the deployed tree corroborates
it as far as a reading of the tree can: the UPDATE above overwrites the column
in place, and the corrected audit row written immediately after it
(durable.ts:1214 at 0e26917) is called with no details_json at all, so no
prior value is recorded anywhere the service can read back. What backups a
particular Cloudflare account keeps is not something this page can measure, and
it does not claim to.
2 · An expiration date on the live service does nothing#
The send wizard asks for a deadline, refuses one in the past, shows it back, and
states in two places — SendView.vue:1332 and EnvelopeDetailView.vue:1104
— that without an expiration date the envelope stays open until completed or
voided. (The refusal is :min="todayIso" on that field, SendView.vue:1300.) The plain reading is that with one it does not. On the deployed build
the date is stored and otherwise ignored, and the two routes differ in a way
worth separating rather than blurring: the send form's INSERT INTO submissions
(durable.ts:1062 at 0e26917) does write expires_at, while the settings
pencil's PATCH never touches it. So the date you set at send time is kept and
never acted on, and the date you set afterwards is not even kept. Nothing ever
expires either way.
Measured 2026-09-01 05:22:14 UTC, on the deployed tree and on the chunks a
recipient's browser actually loads. At 0e26917, service/wrangler.jsonc has
no crons key, service/src/worker.ts has no scheduled export, and
the PATCH route the pencil calls contains no expires_at write. Nothing
sweeps; no envelope ever reaches the expired status. That shows through to the front end: the served
ExternalSignView-CmDSqiPb.js (200, 5 250 bytes), which is what an emailed
signing link opens, carries four closed-envelope cases and no expired one —
already signed, complete, voided, declined — so a past-due envelope
still reports open, still shows its landing page, and still mails the signer a
verification code — 6-digit, which is that same chunk's own word for it, three
times.
Where it does stop, and why that is the point rather than a mitigation.
After the code, the shared signing view refuses: the served
SignView-BM1dkPaZ.js (200, 46 980 bytes) computes
r=n!=null&&new Date(n).getTime()<=Date.now() from expires_at and blocks with
This envelope reached its expiration date and can no longer be signed. That
check is client-side only, and the deployed source's own comment beside it
(SignView.vue:480 at 0e26917) says the server rejects /complete the same
way — which at that commit it does not. So the deadline is enforced against
the party using the app and not against the party addressing the API directly.
This card does not tell you a past-due envelope is unsignable on the live
service; it tells you the service is not what stops it.
Merged, and not deployed.
2471a29 adds the
hourly cron trigger and the scheduled sweep, joins expired to isTerminal()
so the guards already in the code refuse it, and starts saving what the pencil
sends.
Pumasi Sign now keeps the expiration date it asks you for
carries a 7-day window closing 2026-09-08 — again
DECISIONS.md Q-035's
own date, reported — and its own second paragraph reads It is merged and it is
not deployed.
Why both are still here, and what this page will not do about it#
Nobody owns deployment. That is
DECISIONS.md Q-012,
open — and explicitly outside CHARTER Part 0's proceed-on-default rule, so
unlike nearly everything else in this project an agent may not proceed on it.
It is not this product's problem alone: as of 2026-09-01 the same entry also
holds the repair for a booking.pumasi.ai page that shows a booker no times at
all, measured on that host and written up on the
Pumasi Booking card. One open question, two
products, and a user meeting a defect on each.
Q-028 counted the repairs already stacked in the undeployed bundle at
0e26917, and each of these two entries places itself in that queue in its own
words rather than leaving this card to do the arithmetic. Q-035: "Q-028
records that two earlier repairs already wait in the same undeployed bundle;
this is the third." Q-037: "Q-028 counted three repairs already waiting in that
undeployed bundle and this is the fourth." So the expiration repair is the
third and the message repair the fourth. This card sets no deadline
on any of those entries, names no deployer, and takes no position on their
defaults. It reports their state, which is all a catalog page is for.
The ordering is the part that generalises. The expiration repair is what makes that pencil start working — which would have driven more senders into it, and every use of it still wiping the message. The two repairs were published eighty-three minutes apart, land in the same bundle, and wait together behind one open question about who carries a merge to a user — an entry raised well before either of them.
Run it#
git clone https://github.com/pumasi-ai/pumasi-sign
cd pumasi-sign
cd service && npm install && npm test
npx wrangler dev
It starts a local Cloudflare Worker with SQLite Durable Objects and pure-core PDF stamping. Open the local port in your browser to create templates, place fields, and collect signatures.
What it solves#
E-signature is bought once and then paid for forever, by the seat, with the
envelope ceiling written where nobody reads it. What the incumbents actually
charge, read from their own pricing pages on 2026-08-31 and recorded with
their sources in pumasi-sign
roadmap/MARKET.md §1:
| Plan | Price as shown | Limit as shown |
|---|---|---|
| DocuSign Personal | $11/month (annual, billed monthly; $132/yr) | 5 envelopes per month |
| DocuSign Standard | $30/user/month (annual; $360/yr per user) | 100 envelopes/user/year |
| DocuSign Business Pro | $45/user/month (annual; $540/yr per user) | 100 envelopes/user/year |
| SignWell Free | $0 | 3 documents/month, 1 sender |
| SignWell Light | $12/sender/month, or $10 billed annually | Unlimited documents |
| SignWell Business | $36/month for 3 senders, or $30 billed annually; extra senders $15/mo | Unlimited documents |
Prices move, and the date is part of the claim: if you find different numbers on those two pages, they are newer than these. What the pages establish is narrow and worth stating exactly — both vendors meter senders, and DocuSign's two mainstream business plans state 100 envelopes per user per year at $30 and $45 per user per month. Neither page offers a self-hosted or source-available option. Nothing here should be restated as "DocuSign costs X" without the plan name attached.
An earlier version of this page asserted that these platforms "charge $25 to
$65 per user monthly", with no citation. Against the pages above on this
date, that range is wrong at both ends. This project has published and then
retracted an uncited competitor number once already (pumasi-booking
0d1674d); the table replaces the sentence rather than repairing it.
Pumasi Sign provides:
- Deterministic Coordinate Stamping: Places signatures, initials, dates, names, text fields, and checkmarks at normalized coordinates directly onto PDF pages.
- Tamper-Evident Audit Certificates: Every executed document automatically appends a cryptographic certificate logging the original and completed SHA-256 hashes, signer IP addresses, user agents, and ISO timestamps.
- Public Zero-Login Recipient Flow: External counterparties sign securely via tokenized links with optional 6-digit email verification codes without creating an account.
- Office 365 Cloud Document Conversion: Native conversion for Word, PowerPoint, and Excel files via cloud rendering APIs.
- Multi-Tenant Design Customization: Organizations can customize their logo, company title, and primary brand colors across all recipient-facing signing portals.
There is no meter behind any of that, and for a plain reason rather than a
generous one: the deployed service contains no quota, plan, billing or
subscription code at all
(VALUE.md §1).
There is nothing to lift when you run it yourself — see the next section for
what that sentence is currently missing.
About the licence, and what this page will not say#
This card's summary line used to end "Apache-2.0." That is not a claim this
page can make today. pumasi-sign is a public repository with no
LICENSE file:
gh api repos/pumasi-ai/pumasi-sign/contents/LICENSE # 404 Not Found
gh api repos/pumasi-ai/pumasi-tunnel/contents/LICENSE # 200 — 10 273 bytes
A public repository with no licence grants no rights at all: you may read the
code, and you have no permission to run, modify or redistribute it. A sibling
product carries the file — pumasi-tunnel, Apache License 2.0, as does this
website's own repository — so this reads as an omission rather than a position
taken.
What changed on 2026-09-01 is not the licence. It is who is being told.
Until that morning this was an internal question about an undeployed page.
At 01:02:16 UTC the product's own landing page went live carrying
Apache-2.0 in three separate claims — the hero strip, the stage banner, and a
comparison-table row headed License & Source Code whose neighbouring columns
read Proprietary Closed Source. That row's entire purpose is to invite a
reader to choose this product because its terms differ from the
incumbents', and the terms it names do not exist. Q-021's own framing — not
yet public, which is the whole reason this is a question and not an incident —
is overtaken by that deploy. This card reports the claim; it does not repeat
it, and one surface saying something untrue is a mistake that two surfaces
would make a pattern.
A note for anyone checking this themselves, because the obvious command was
lying until recently and this page said so. Through 2026-08-31,
gh repo view --json licenseInfo returned null for every repository in
this organisation, including pumasi-tunnel and pumasi-web, which
demonstrably do carry an Apache-2.0 LICENSE — so a null there was not
evidence of absence, it was no evidence at all. Re-checked by this card at
2026-09-01 05:26:39 UTC, that is no longer true: GitHub's detection has caught
up and now reports {"key":"apache-2.0"} for both of those repositories, and
null for pumasi-sign.
The conclusion is unchanged and the reasoning is worth keeping anyway. That
null is the evidence cited in pumasi-sign's own STAGE.md §2.3 and in
Q-021, and on the day it was cited it would have read identically for a
correctly licensed repository — a test that cannot fail is not a check.
It happens to be corroborated now, which is luck rather than method. The
question the file answers is still the one to ask: gh api repos/pumasi-ai/pumasi-sign/contents/LICENSE → 404, above, re-run for this
card at 2026-09-01 05:22:52 UTC.
Which of those it is, is not this page's call. It is open as
pumasi/DECISIONS.md
Q-021, whose named default is to add the Apache-2.0 file and honour the
claim, and whose named alternative is to remove the claim instead. That entry
records that the project's usual proceed-on-the-default rule is deliberately
not taken here, because publishing a licence is an outward grant that third
parties may then rely on — the one kind of file edit that is not really
reversible.
So this page takes the move that prejudges neither answer: it stops stating a licence as a fact, and states the situation instead. When Q-021 lands, this section becomes one sentence.
Inside the Product#
The screenshots below are of the deployed application at
sign.pumasi.ai, which answers GET /api/health
with 200 {"status":"ok","service":"pumasi-sign", …}.
You can go and try it. An earlier version of this page told you not to,
citing a report that sign-up and sign-in were both broken. That report —
#9 — was closed not planned on 2026-08-31 16:04 UTC, and closed on evidence rather than on an
inability to reproduce: the worker that serves this host cannot emit a 403
anywhere in service/src/, its establishSession
(service/src/durable.ts:655) has no domain gate and creates an account for
any verified email address, and the reporter's exact wording occurs once in the
whole fleet — in a different product.
The defect this page carried at the top for two days is closed, and it is
worth a line rather than a silent deletion.
#7, priority: high: a
person who signed out saw one button, Sign in again, and pressing it handed
them {"error":"Endpoint not found"} rendered raw as a document. It was
closed 2026-09-01T00:59:12Z, and — the part that matters, because closing
an issue serves nobody by itself — the repair reached users in the deploy
at 2026-09-01 01:02:16 UTC — three minutes after the tracker said so, not
before. Alongside it,
#8, the app root having no
product page in production, was closed 2026-09-01T00:59:11Z and made true
by the same deploy.
Verified on the live host rather than on the tracker, 2026-09-01 02:40 UTC:
$ curl -s https://sign.pumasi.ai/ | grep -o '/assets/index-[^"]*\.js'
/assets/index-CnoFAC2c.js
$ curl -s -o /dev/null -w '%{http_code} %{content_type}\n' \
'https://sign.pumasi.ai/login?next=%2F'
200 text/html
The served SignedOutView-C2J9s3yp.js builds that button's target by calling
the main bundle's exported helper with /, and the helper is
function ml(e){return`/login?next=`+encodeURIComponent(e)}. Decisively, the
helper that built the broken URL is gone from the shipped JavaScript:
grep -c '/api/auth/login?next=' index-CnoFAC2c.js returns 0.
One thing not to over-read, because the obvious check still looks alarming.
GET /api/auth/login?next=%2F on that host still answers 404 — and that
is now correct behaviour rather than a defect. Nothing targets it any more. A
route that never existed on the worker returning 404 is a server doing its
job; the defect was a button pointing at it, and the button no longer does.
What has not changed is the thing underneath both issues: nobody owns
deployment. That is Q-012, open, exactly as on the sibling
Pumasi Booking card. This deploy was not announced
in any queue and matches no release note; it had to be identified from the
artefact, which STAGE.md §2.2 does by chunk fingerprint, landing on
0e26917 — built
from a working tree five seconds before that commit's own timestamp. And
main has moved past it by nine commits since — git rev-list --count 0e26917..f0d1912 against a freshly fetched origin/main, run by this card on
2026-09-01 — of which two change service/src/durable.ts, the file that
answers this host:
2471a29 and
9659e69. Those are the two repairs the section Two defects are
live on the hosted service measures, and the reason this paragraph is not a
piece of housekeeping: the repository is ahead of the deployment by two defects
a user is meeting today, and nothing carries one tree to the other. Someone
deployed once; the question of who owes you the next one is still open.
The sign-in repair is written up in
a release note
whose own first line reads "It is merged and it is not deployed" — a
can-hurt release under
DECISIONS.md Q-027,
veto window to 2026-09-07, which the steward has yet to confirm. That note is
written in the present tense and describes the branch on the day it was
published; the deployment has since overtaken it. Read a release note as a
claim about the repository and check the deployment separately — and note
what the window now means: seven days to veto a change users already have.
What is queued next is a question for
roadmap/BACKLOG.md,
and this page quotes no rank from it. That file was reordered three times on
2026-08-31 alone; a rank in prose is the same kind of cache as a test count, and
the one this page used to publish was spent before you read it. Read the file.
See Before you use it at the top of this page for what is still worth knowing
before you rely on this — as of 2026-09-01 that is two live defects in the
deployed worker and the licence claim, no longer the front door. The local
wrangler dev route above is unaffected by any of it, and neither defect is in
the main you would clone.
1. Document Dashboard & Action Queue#
Manage incoming signature requests, active contracts, and completed envelopes in one centralized view:

2. Multi-Signer Send & Preparation Wizard#
Upload agreements, assign signers, configure signing orders, and place drag-and-drop fields:

3. Front-End Design & Branding Customizer#
Customize organization logos, brand accent colors, and recipient welcome banners with live real-time preview:

4. Frictionless Recipient Verification#
External signers receive secure tokenized links with 6-digit email verification codes—no account creation required:

Cloudflare Edge Native#
Pumasi Sign is built to run at the edge with zero dedicated container overhead:
- Pure JavaScript / WebAssembly PDF Engine: PDF parsing and stamping execute inside Cloudflare V8 isolates with zero C++ or heavy OS binary dependencies.
- Transactional SQLite Storage: Envelopes, signers, templates, and document blobs are stored in transactional Cloudflare Durable Objects.
- Custom Domain Deployment: Deployed globally at
sign.pumasi.ai, which answered200on/api/healthwhen this card was written (2026-09-01 02:40 UTC). Since 2026-09-01 01:02:16 UTC that host also serves the product's own landing page at/(#8, closed), so this catalog card is no longer the only public surface this product has. The live bundle is still behindmain, andalpha's exit gate above still reads NOT MET — for what that landing page says, no longer for its absence.
One thing this section cannot tell you yet, and says so rather than
implying otherwise. sign.pumasi.ai is served by the Cloudflare Worker in
service/, not by the FastAPI application in backend/ — verified by the
shape of its own error bodies, and open as pumasi/DECISIONS.md Q-018
(which implementation is Pumasi Sign?). This page previously added that CI does not gate
service/. That is no longer true, and the correction is not a softening.
Since ef851d6 —
committed as "a job for the tree users actually meet" —
.github/workflows/ci.yaml runs a fourth job that builds the worker and runs
its suite, with a step that asserts the suite actually ran rather than
trusting exit 0. What that job runs changed in kind this week, not in
degree. Until 3d01198
— "the deployed tree's front door, recorded" — every case under
service/src/test/ exercised one pure function, the PDF stamper, and nothing
that answers a request. That commit is the first in this repository's history
to construct the Durable Object that serves sign.pumasi.ai and drive it
through its own fetch(): schema initialisation, establishSession, the
session cookie's shape, single-use codes, and the 404 an unknown route is
given. The cross-family review that approved it
(reviews/20260831-160155-code-gemini.md,
VERDICT: APPROVE) ran the suite rather than reading it.
No test count is quoted here. cd service && npm test prints the current
totals; a number copied into prose is a cache with no way to invalidate it —
the rule the Pumasi Booking card states about its
own suite, and this page carried the stale number that proves it. And none
of this is an upgrade announcement. The coverage reached the worker's front
door while issue #7 was live at that front door in production; the new cases
characterise what the deployed tree does, they do not repair it.
So "CI is green" is now a broader claim than it was and still not a statement
about what you would meet at that domain — and run
33420378497
shows exactly how. At ef851d6 it is green on all four jobs — backend,
frontend, service, e2e — while the Sign in again button on this
product's own signed-out page was returning 404 in production, measured from
this card at 18:49 UTC that same day and not repaired for users until the
deploy at 2026-09-01 01:02:16 UTC. Four green jobs sat above a live
entry-path defect for the better part of a day, and that is the argument
rather than the anecdote. The e2e job is the one to sit with: it is green
because frontend/playwright.config.ts drives backend/ — locally a
uvicorn process, in CI a container built from the root Dockerfile, and both
are backend/, the one tree in which GET /api/auth/login exists. Its
Playwright specs exercise a sign-in path that works, on a server no user
reaches, and would keep passing however long the defect stayed live
(STAGE.md §2.1).