Seed catalogue · Feedback

Penny

A self-hosted customer-feedback decision system that connects attributed evidence, accountable product decisions, customer-safe roadmaps and releases, and requester outcomes for one B2B software business.

Alternative to
Canny, under MIT
Domain
request with attributed evidence
Version
0.0.0-bootstrap
Contract
85 clauses across 9 areas
Maturity
ui-port-read-spine
Penny screenshot 1: landing
Penny screenshot 2: public roadmap
Penny screenshot 3: operator overview
Penny screenshot 4: operator decision

4 screenshots from the product's own visual and verification artifacts. Its handover records what was exercised.


What you get

Everything below is generated from the seed's own manifest and contract. No rounded-off claims, no numbers we made up.

Operating cost

Runs on your cloud's free tier

The seed declares the free plan as the default deployment. Its own estimate, thresholds and sources are below, unedited.

Status
estimate-only
Basis
Workers Free is the default Cloudflare plan; Workers Paid starts at USD 5 per account-month. The current read-only port dispatches no Queue deliveries. For the future release-delivery limit, a normal Queue message at or below 64 KB is at least three operations (write, read, delete): 3,334 successful deliveries consume at least 10,002 operations and exceed Workers Free's 10,000 Queue operations per account per UTC day. A maximum 50,000-recipient release needs at least 150,000 Queue operations before retries, dead-letter handling, or messages above 64 KB, so it genuinely requires Workers Paid. Workers Free D1 includes 5 million rows read/day, 100,000 rows written/day, and 5 GB total storage; reaching a Free D1 limit makes D1 return errors until the UTC reset or an upgrade. R2 Standard includes 10 GB-month storage, 1 million Class A and 10 million Class B operations/month; using R2 requires a billing-enabled Cloudflare account even while usage remains within those included allowances. The R2 free tier does not apply to Infrequent Access storage, which also has retrieval fees and a 30-day minimum storage duration. Cron Triggers are included; this port does not currently use Durable Objects. R2 and Workers do not charge Internet egress under the documented plans. Transactional email remains a separate provider and message-volume cost.
Monthly Usd
  • Cloudflare Workers Free by default; Workers Paid starts at 5, then Queue, D1, R2, and Worker usage above included allowances are usage-dependent
  • Transactional Email provider and message-volume dependent
Excluded
domain registration, operator implementation time

Externals declared

1 external, each with a reason

Under BASE-DATA-004, user data leaves only through what is declared here.

transactional-email
Operator magic-link sign-in (BASE-ACCESS-001), requester email verification (REQ-023), decision-review alerts (NOTIFY-003), and opted-in requester status and release notifications must reach addresses outside the deployment; no owned Cloudflare primitive delivers mail. The adapter must accept a caller-supplied idempotency key, enforced at the provider, so REL-006 can retry an ambiguous send without duplicating it, and must deliver signed delivery, bounce, and complaint callbacks so REL-007, REL-011, and REL-013 can update delivery state.

Extension points

40 declared integration conveniences

These hooks are optional conveniences. Owners may change any source, including core logic, schema and infrastructure; their agents adapt useful upstream fixes and verify the result.

app.page.v1app.navigation.v1app.settings-section.v1job.consumer.v1cron.task.v1operator.management.v1request.moderation.v1request.visibility.v1request.vote-eligibility.v1decision.commitment.v1roadmap.publication.v1release.recipient-eligibility.v1requester.notification.v1evidence.created.v1evidence.linked.v1request.created.v1request.moderated.v1request.published.v1request.declined.v1request.merged.v1request.unmerged.v1decision.committed.v1decision.revised.v1decision.review-due.v1roadmap.status-changed.v1roadmap.status-update-published.v1release.published.v1release.delivery-failed.v1release.outcome-recorded.v1overview.after.v1evidence.list.after.v1evidence.detail.after.v1account.detail.after.v1request.detail.after.v1decision.brief.after.v1decision.alternatives.after.v1roadmap.item.after.v1release.detail.after.v1portal.request.detail.after.v1mail.sender.v1

Declared limits

Capacity, stated up front

Straight from seed.json → limits. Where a value is an estimate the seed says so.

Provenance
All values are estimates for backend design and must be replaced by load-test results before the first release. Latency that defines a feature lives in its contract clause, not here.
Status
estimated-not-tested
Operators
20
Boards
100
Tags
500
Accounts
25,000
Requesters
250,000
Evidence Items
1,000,000
Public Requests
100,000
Votes
2,000,000
Comments
1,000,000
Decisions
25,000
Roadmap Items
25,000
Releases
10,000
Recipients Per Release
50,000
Public Read Requests Per Minute Per Ip
120
Public Write Requests Per Minute Per Ip
30
Public Writes Per Minute Per Identity
20
Identity Actions Per Minute Per Ip
10
Identity Verifications Per Hour Per Email
5
Magic Link Requests Per Hour Per Email
5
Mail Events Per Minute Per Ip
600
Request Submissions Per Hour Per Identity
10
Upload Bytes Per File
10,485,760
Upload Bytes Per Request
26,214,400
Upload Types
image/png, image/jpeg, image/gif, application/pdf

Non-goals, stated proudly

A product that says what it will never do is a product you can trust to stay small, fast and legible. These are refusals, not gaps.

  • Multiple workspaces, businesses, or tenants
  • Granular roles, permissions, and approval chains
  • A CRM, support inbox, survey, NPS, community chat, or general-purpose forum
  • Anonymous or unverified voting
  • AI classification, summarisation, scoring, prioritisation, or automatic merging
  • A combined priority score or automatic ranking of requests, bets, or roadmap items
  • Automated CRM, support, review-site, or call ingestion
  • Importing feedback from another feedback tool
  • A script-tag feedback widget or in-app capture SDK beyond the embeddable portal in REQ-027
  • Bidirectional Jira, Linear, or other delivery-tracker synchronisation
  • Portfolio capacity planning, Gantt charts, sprint management, and public delivery estimates
  • Account-specific public roadmaps
  • Native mobile applications

The contract

All 85 clauses of Penny, from its own CONTRACT.md. Stable IDs identify requirements; the source handover and verification evidence distinguish implemented, tested and uncovered behavior.

HOME1 clause

HOME-001
Public feedback portal.

Penny serves the deployment's public feedback portal at /. It identifies the workspace and gives the owner's configured one-line portal description; public feedback, roadmap, and updates follow REQ-001, ROAD-006, and REL-010. It makes no vendor, pricing, testimonial, or metric claims and exposes no operator-only data.

OPS4 clauses

OPS-001
Equal operators.

Every operator can see and act on every account, evidence item, request, decision, roadmap item, and release except where a policy restricts an action. There are no roles.

OPS-002
Management.

Operators are invited and removed in settings. The operator configured at setup can be removed only by themselves. Policy: operator.management.v1; default: any operator may invite or remove any other operator.

OPS-003
Overview.

The operator home lists decisions that are ready or review due, evidence that is unreviewed, roadmap items that are Planned or In progress, and releases published in the last 30 days with their outcome totals. Every figure links to the records it counts, and a section with no records names what is absent and links to the action that creates one.

OPS-004
Global search.

Operators can search request titles and descriptions, evidence wording, decision questions and rationales, roadmap item titles and summaries, release titles and bodies, requester display names and email addresses, and account names. The first page of results returns within 500ms.

EVID10 clauses

EVID-001
Capture.

Every portal submission creates exactly one evidence item in the same transaction (REQ-003). An operator can also record an evidence item, whose source is one of sales call, support conversation, interview, email, or a free-text label the operator supplies.

EVID-002
Provenance.

Every evidence item stores its source, its original wording, the observed time, and the operator or portal submission that created it. The submitting requester or named speaker and the account are nullable, are set only from what the recorder supplies, and are never inferred.

EVID-003
Review lifecycle.

New evidence is unreviewed. An operator can link it to a request or to a candidate bet in a decision brief (DEC-002), create a request or a decision from it, or mark it reviewed with a reason, and can return any reviewed item to unreviewed. Linking alone does not mark it reviewed.

EVID-004
Non-destructive linking.

One evidence item can be linked to any number of candidate bets, and each link is recorded as supporting or contradicting that bet. Linking, unlinking, tagging, merging a request, and committing a decision never alter or remove an evidence item or any of its versions; only an operator deleting it does.

EVID-005
Account context.

An account has a name, an internal owner, optional commercial fields for revenue, renewal date, and health, its requesters, its evidence in observed-time order, and its linked requests and decisions. Evidence recorded against an account appears on that account, and on every request and decision the same transaction linked it to, when that transaction commits.

EVID-006
Private commercial data.

No field of an account (EVID-005), no evidence source attribution, and no internal note (REQ-032) appears in a portal response, on a public page, in page metadata, in a sitemap, or in the subject or body of a requester notification.

EVID-007
Duplicate suggestions.

When an operator records evidence or a requester types a request title, Penny returns within 500ms up to 5 existing requests ranked by term overlap between the typed title and stored request titles and descriptions, and names the matched terms for each. A requester is shown published requests only. Matching is lexical and deterministic; Penny never links or merges without an explicit operator or requester action.

EVID-008
Evidence attachments.

An operator can attach files to an evidence item within the limits in seed.json under BASE-INPUT-003. An evidence attachment is readable only by an operator session and is never served from a public path, whatever the publication state of a linked request.

EVID-009
Tags.

Operators create, rename, merge, and delete tags and apply them to evidence items and requests. A tag is operator-only, filterable in every operator list, survives a request merge, and is included in the export.

EVID-010
Versions.

Editing an evidence item's wording writes a new version and leaves every earlier version readable to operators. The item's own ID never changes.

REQ32 clauses

REQ-001
Public portal.

The portal serves published requests, a request detail page with its discussion, the public roadmap, and published releases. Policy: request.visibility.v1; default: any visitor may read all four without signing in.

REQ-002
Requester identity.

Submitting, voting, commenting, following, subscribing, and recording an outcome each require either an email address verified under REQ-023 or a host-signed identity under REQ-031. A portal response shows the requester's chosen display name and never their email address or account.

REQ-003
Submission.

A requester submits a title, a problem description, an optional free-text proposed solution, an optional free-text impact statement, a board (REQ-018), and optional attachments. The submission atomically creates one unreviewed evidence item and one request awaiting moderation; the request is not public until an operator publishes it. Policy: request.moderation.v1; default: an operator reviews every requester submission, edit, and comment before it becomes public.

REQ-004
Duplicate prevention.

Before a requester submits, the portal shows the suggestions from EVID-007 with a vote control and a follow control on each. Choosing one applies that vote or follow and discards the draft, creating no request, evidence item, or moderation item.

REQ-005
Publication.

An operator publishes a moderated request by approving its public title, description, board, requester display name, and each attachment separately (REQ-011). The published record contains only the approved fields.

REQ-006
Voting.

A requester identity has at most one vote on a request and can remove it. The vote total is the count of distinct current voters, and a vote is accepted only while the request's public status is Open, Under review, Planned, or In progress (REQ-016). Policy: request.vote-eligibility.v1; default: any verified requester may vote and every vote counts once.

REQ-007
Discussion.

Verified requesters and operators can comment on a published request. A requester comment is held under request.moderation.v1 and becomes public only when an operator approves it; an operator comment is public when posted.

REQ-008
Merge.

An operator can merge a duplicate request into the canonical request, which is the one that survives. The operation atomically moves the duplicate's evidence links, approved attachments, comments, and followers, collapses the combined votes so each identity counts once, keeps the duplicate's title and source as merge history, and answers the duplicate's public URL with a 301 to the canonical request. Merging a request that is itself a duplicate merges it into the canonical request at the end of its chain.

REQ-009
Unmerge.

An operator can reverse a merge. Penny restores the duplicate request's own content, votes, comments, followers, and evidence links, leaves anything added to the canonical request after the merge on the canonical request, and records the reversal.

REQ-010
Follow and consent.

A verified requester can follow and unfollow a published request. Submitting and voting present an unticked "Email me updates" control and grant no email permission on their own. Following, or ticking that control, stores a timestamped permission record for the verified address, which is what makes the identity eligible under REL-004.

REQ-011
Portal attachments.

A file submitted with a request is operator-only until an operator approves that file for publication; rejecting a file records a reason for operators and does not reject the request. While both the parent request and the file are published, GET /api/public/attachments/:id serves it with its declared content type and, for the image types in seed.json → limits.uploadTypes, an inline disposition so it renders on the request page; this supersedes BASE-INPUT-003's download disposition for published image attachments only. In every other state that path returns 404.

REQ-012
Portal search and browse.

Visitors can search published request titles and descriptions, browse one board at a time, filter by public status, and sort by vote total or by most recent public activity, meaning the request's latest published comment, status update, or vote. The first page returns within 500ms, and no result, count, or facet includes an internal board, an unpublished request, or a held comment.

REQ-013
Submission idempotency.

A public submission accepts an idempotency key. Repeating the same submission with that key returns the original request and evidence result and creates no second record of any kind; the same key with a different body returns 409. Keys are retained for 30 days and hold no personal data.

REQ-014
Identity failure.

A host-signed identity is expired when its signed issue time is more than 24 hours old or more than 5 minutes in the future. An identity that is expired, or whose signature does not verify, is rejected for every identity-required action, changes no data, and reveals no account or identity match; the visitor is offered email verification (REQ-023) instead.

REQ-015
Status updates.

An operator can publish a status update on a published request. It is rendered distinctly from comments, can be pinned above them, enters the request history, and notifies followers under NOTIFY-001.

REQ-016
Public status.

A published request has exactly one public status: Open, Under review, Planned, In progress, Shipped, or Declined. A declined request is Declined; otherwise a request with an approved roadmap mapping takes the public status its mapped roadmap item projects under ROAD-003, and a request without one is Open.

REQ-017
Decline.

An operator can decline a published request. A decline without a public reason is rejected. Declining sets the public status to Declined, adds the reason to the request history as a status update (REQ-015), and refuses new votes and requester comments while leaving existing ones visible and counted. Reversing a decline restores the request's previous public status.

REQ-018
Boards.

Every request belongs to exactly one board. Operators create, rename, reorder, and archive boards in settings and can move a request to another board, which keeps its votes, comments, followers, and evidence links. An archived board leaves portal navigation, keeps its published requests reachable at their URLs, and refuses a new submission with 409.

REQ-019
Internal boards.

Each board is public or internal. A request on an internal board never appears in a portal response, on the public roadmap, in a release, or in a requester notification, whatever its own publication state. Moving a request to an internal board withdraws it from the portal within 5 seconds and its public URL returns 404.

REQ-020
Requester's own content.

A requester can edit the title and description of their own request and the text of their own comment. An edit to already-public text re-enters moderation under request.moderation.v1, and the previously approved text stays public until the edit is approved or rejected.

REQ-021
Requester withdrawal.

A requester can withdraw their own request. A withdrawn request leaves moderation or the portal, its public URL returns 410, and its votes stop counting. Its evidence item survives under EVID-004, marked withdrawn.

REQ-022
Blocking a requester.

An operator can block a requester identity. A blocked identity's submissions, votes, comments, follows, and outcome responses are refused with 403; their published requests and comments are hidden from the portal but kept for operators; and their votes stop counting toward totals. Unblocking restores all of it.

REQ-023
Email verification.

A requester verifies an email address by following a single-use link sent to it; the link expires 30 minutes after it is sent and fails after one use. The verified session is a browser-local token created at the requester's first interaction and expiring after 30 days without use, so no portal surface sets a cookie. An identity-required action attempted before verification is held for 24 hours, applied on the first verification of that address, and otherwise discarded, and it creates no vote, public record, or notification while held.

REQ-024
Requester identity merge.

An operator can merge two requester identities. The survivor keeps both verified addresses and all requests, comments, follows, permission records, and suppression state; votes for the same request collapse to one; and the merge is recorded. A merge creates no new vote and no new permission record.

REQ-025
Requester activity.

A verified requester can see their own requests with their current moderation state and public status, their votes, their follows, and their own comments including held and rejected ones, and can unfollow, mute, or change their update subscription (REL-012) from that view.

REQ-026
Demand attribution.

For any request, operators can see every current voter's display name, verified address, and linked account, and the vote total broken down by account. The operator view shows the vote total, the linked-evidence count, and the distinct-account count as three separate figures and combines them into no score. None of it appears in a portal response.

REQ-027
Portal embedding.

The portal can be embedded in an iframe on the origins in publicOriginAllowlist under BASE-PUBLIC-002; a request to frame it from any other origin is refused.

REQ-028
Request queue.

Operators can list every request, including unpublished ones, filtered by moderation state, board, public status, tag, linked decision, and account, and sorted by vote total, most recent activity, or age. The first page returns within 500ms.

REQ-029
Operator-authored request.

An operator can create a request from one or more evidence items. It skips moderation, records the operator as its author, links the evidence it was created from, and becomes public only through REQ-005.

REQ-030
Moderation verdicts.

An operator can approve, edit and then approve, or reject with a reason any request or comment awaiting moderation. A rejected item never becomes public, counts toward no public total, stays visible with its reason to its author and to operators, and notifies its author only when the operator chooses to.

REQ-031
Host identity claims.

A host-signed identity may assert a display name, an email address, and an account identifier and name. A valid signature marks that address verified without a further challenge and links the requester to the matching or newly created account; any asserted commercial field is stored operator-only under EVID-006.

REQ-032
Internal notes.

Operators can add a note to any request. A note is rendered distinctly from an operator comment and never appears in a portal response, page metadata, a release, or a notification.

DEC8 clauses

DEC-001
States and queue.

A decision is draft until its brief has a question, an owner, at least one candidate bet, and at least one linked evidence item, at which point it is ready; committing makes it committed, and DEC-007 makes a committed decision review due. The decision queue lists every decision in each state with its owner, last action, review date, and linked-evidence count, and a decision changes row rather than leaving the queue when its state changes.

DEC-002
Brief.

A candidate bet is one named option a decision could commit to, and a bet has at most one internal roadmap item. A decision brief holds a question, an accountable owner, its linked evidence and accounts, its candidate bets with the counter-evidence and assumptions for each, a confidence level of low, medium, or high, a review date, and the requesters currently eligible under REL-004 through its mapped requests.

DEC-003
Comparable bets.

Every candidate bet in a brief shows the same dimensions: affected requesters and accounts, the count and most recent observed time of its supporting evidence, its contradicting evidence, urgency, delivery shape, and an observable success measure. Penny stores and displays no combined score and no automatic ranking of the bets.

DEC-004
Commitment.

Committing requires a selected bet, a rationale, an owner, a confidence level, and a review date, and committing a brief with fewer than two candidate bets additionally requires a recorded reason why no viable alternative exists. The operation records the declined bets and an evidence snapshot made of the evidence IDs, evidence-version IDs, and DEC-003 dimensions as they stood at that moment, without altering the evidence itself. Policy: decision.commitment.v1; default: any operator may commit.

DEC-005
Commitment effects.

Committing atomically marks the decision committed, creates or updates the selected bet's internal roadmap item as Planned, leaves the declined bets' roadmap items unchanged, and links the decision to that roadmap item. Commitment writes no public field itself; whether the new status reaches the portal is decided entirely by ROAD-004 and ROAD-005.

DEC-006
Revision.

A later change of selected bet or rationale under decision.commitment.v1 supersedes the prior commitment as a new decision revision, and the original rationale, evidence snapshot, and declined bets stay readable. A revision that changes the selected bet returns the superseded bet's roadmap item to Exploring and, where that item was published, withdraws its approval under ROAD-004 and prepares a status update draft under ROAD-007.

DEC-007
Review due.

A review is complete when an operator records a review note and either a new review date or a revision (DEC-006). A committed decision whose review date has passed in workspaceTimezone without a complete review becomes review due within 1 hour and its owner is notified under NOTIFY-003. Becoming review due changes no roadmap status, no public status, and no selected bet.

DEC-008
Outcome evidence.

A requester outcome (REL-008) and any evidence recorded against a linked release appear on the decision as post-release evidence within 60 seconds and do not alter the evidence snapshot that supported the commitment.

ROAD8 clauses

ROAD-001
Internal states.

A roadmap item is Exploring, Planned, In progress, Shipped, or Dropped. A status change records the actor, time, and reason and adds an activity entry to the linked decision without changing that decision's state. Moving backward is allowed and is recorded as a further transition.

ROAD-002
Internal record.

A roadmap item holds its owner, target or release window, linked decision and evidence, account impact, confidence, delivery notes, and its customer-safe title, summary, and status.

ROAD-003
Public projection.

An approved roadmap item's public status follows its internal status within 5 seconds of a transition: Exploring is shown as Under review; Planned, In progress, and Shipped keep their labels; a Dropped item is withdrawn from the public roadmap. The projection exposes only the approved title, summary, board, and public status; account names, revenue, internal dates, confidence, rationale, evidence, declined bets, and delivery notes are never projected.

ROAD-004
Approval is publication.

A roadmap item and each of its request mappings are internal until an operator approves their customer-safe fields, and that approval alone is what puts them on the public roadmap and on the mapped requests. A published request has at most one approved mapping; approving a second is rejected. Withdrawing approval removes the projection from the portal within 5 seconds, makes its public roadmap URL return 404, and deletes no internal record. Policy: roadmap.publication.v1; default: an operator approves every public mapping explicitly.

ROAD-005
Commitment mapping.

The roadmap item DEC-005 creates or updates is internal unless it already carries an approval under ROAD-004. An already-approved item projects its new Planned status under ROAD-003; an unapproved item and its mappings stay private, and commitment never grants an approval.

ROAD-006
Public roadmap.

The public roadmap groups approved items under Under review, Planned, In progress, and Shipped and links each to its public request or release. It exposes no unapproved item and no count that includes one.

ROAD-007
Status update draft.

Every status change of a roadmap item that carries an approval under ROAD-004, and every request decline under REQ-017, prepares a draft customer-safe update naming the new public status and the problem it addresses. The draft carries no internal rationale, commercial context, declined bet, or outcome prompt, and is invisible on the portal until an operator publishes it.

ROAD-008
Status update publication.

Publishing that draft adds it to the history of every approved mapped request and queues one notification per update version for each requester eligible under REL-004 who follows one of those requests, deduplicated by identity and subject to the coalescing in NOTIFY-002. Republishing the same version queues nothing further.

REL13 clauses

REL-001
Draft and targets.

A release draft has one or more targets, each linking one roadmap item in any state to its decision and its approved public request mappings. The draft also holds a customer-safe title, a type of New, Improved, or Fixed, one or more subject tags drawn from EVID-009, a body, a publication date, an optional setup or usage link, an author, and the wording of the outcome prompt shown with the REL-008 response.

REL-002
Publication.

An operator can publish a draft only when it has a title, a body, at least one target, and every linked roadmap item at Shipped; publishing with any linked item in another state is rejected. Publication makes one versioned update visible on the portal and durably records the complete recipient snapshot (REL-004) before it queues any delivery.

REL-003
Published wording.

Published release wording is never overwritten. A correction creates a new version, keeps the earlier version readable by operators, and marks which version the portal shows. A correction changes the portal only; sending it to recipients requires an operator to send it as a new delivery version.

REL-004
Recipient eligibility and snapshot.

A recipient is eligible when they hold a verified address and a current permission record, and each eligible identity appears exactly once in the snapshot however many target contexts it has. At publication Penny snapshots every release target and each recipient's linked request or evidence context, and a later merge, relink, or preference change does not alter that snapshot. Policy: release.recipient-eligibility.v1; default: a permission record comes only from following a request (REQ-010) or subscribing to updates (REL-012); voting alone and an operator-entered address grant nothing.

REL-005
Customer-safe delivery.

Release email and release portal content carry only the published release fields and the recipient's own request context. Recipient lists, other requesters, account context, commercial impact, decision rationale, confidence, and declined bets never appear in either.

REL-006
Acceptance and retry.

Publication queues each eligible delivery within 60 seconds of its permitted send time (NOTIFY-002). A failure returned before provider acceptance is retried up to 5 times with exponential backoff over 1 hour and then marked failed. After acceptance Penny never resends automatically. A result the provider leaves ambiguous is marked unknown, shown to operators, and retried under its delivery key (REL-007); a delivery still unknown after those retries is marked failed for BASE-OPS-004. The published update stays visible on the portal in every failure state.

REL-007
Idempotent delivery and provider events.

Every release version and recipient has one durable delivery key that mail.sender.v1 passes to the provider as an idempotency key, and the accepted provider message ID is stored against it. /api/public/mail/events accepts only signed provider callbacks and deduplicates their event IDs, which are retained for 30 days.

REL-008
Outcome response.

A requester in a release's publication snapshot can answer Adopted, Partly adopted, or Not yet for each of its targets, whether or not they have muted or unsubscribed from email. Penny stores one current response per release, requester, and target; changing it records a revision and updates the aggregates without exposing the requester on any public surface. Repeating the same response records no revision.

REL-009
Outcome coverage.

Operators can see, per target and for the whole release, the count of each response and the coverage, meaning current responses divided by snapshot recipients, and can see the individual responses. Each current response links through its snapshotted target and recipient context back to the applicable request or evidence, roadmap item, and decision.

REL-010
Empty feed.

With no release published, the public updates surface states that there are no updates yet and renders no draft, example, or placeholder entry.

REL-011
Bounces and complaints.

A hard bounce or a spam complaint suppresses the address: no further Penny email reaches it until the requester verifies it again, and its permission records are marked suppressed rather than deleted. A soft bounce marks that one delivery failed and suppresses nothing. Suppression removes no vote, request, comment, or portal access.

REL-012
Update subscription.

A verified requester can subscribe to every published release from the public updates surface and unsubscribe there or from any notification's unsubscribe link. Subscribing stores a timestamped permission record for that address; unsubscribing withdraws it without removing votes, requests, or follows.

REL-013
Delivery states.

A delivery moves through queued, then accepted, then delivered, and can terminate at failed, suppressed, or unknown. An out-of-order provider event never moves a delivery backward along that order and never counts a result twice.

NOTIFY5 clauses

NOTIFY-001
Requester updates.

A follower of a published request is notified when an operator publishes a status update on it (REQ-015), declines it (REQ-017), or publishes a status update mapped to it (ROAD-008), unless they have muted that request or their address is suppressed. An internal roadmap transition with no published update notifies nobody, and republishing the same update version notifies nobody again.

NOTIFY-002
Preferences, timing, and unsubscribe.

A notification's permitted send time is the first moment after it is queued at which the requester's preferences and the quiet hours below allow it. Every requester notification carries a one-click unsubscribe link that works without signing in, and a requester can mute one request or all requester email; the preference is checked immediately before each send and removes no vote, evidence, or portal access. Policy: requester.notification.v1; default: hold requester email from 20:00 inclusive to 08:00 exclusive in workspaceTimezone, and coalesce updates for the same request queued within 1 hour into one message.

NOTIFY-003
Decision review.

A decision owner receives one email and one in-app notification, which stays in their notification list until dismissed, the first time a decision becomes review due. Operator email carries no quiet hours and queues within 60 seconds of the decision becoming review due. No repeat is sent until the review date changes or the operator completes the review and schedules another.

NOTIFY-004
No mail loops.

Notifications are sent from a no-reply address. Penny exposes no inbound-mail route, so a reply can create no evidence item, comment, request, or other record.

NOTIFY-005
Notification failure.

Requester status-update email and operator decision-review email each use a durable recipient-and-event key and retry, suppress, and expose failure exactly as REL-006, REL-011, and REL-013 describe. The in-app or portal record of the same event stays available when the email fails.

DATA4 clauses

DATA-001
Export contents.

The export contains every durable record as JSON, including every operator, requester, identity merge, account, board, tag, request, moderation decision and reason, merge history, vote, comment, note, follower, block, evidence item and version, decision and revision, roadmap item and transition, release, release version and target, delivery with its key and provider message ID, outcome and revision, subscription, suppressed address, notification and preference, custom field, and audit entry, plus every attachment as its original file.

DATA-002
Public/private boundary.

No operator-only field name and no operator-only value appears anywhere in a public response payload, including in fields the portal does not render.

DATA-003
Erasure outranks history.

Where a clause here says a record is preserved, versioned, immutable, or never overwritten, erasure under BASE-DATA-003 takes precedence: the record is deleted or anonymised and the surviving audit entry keeps an anonymised reference. Unmerging (REQ-009) and revision history never recreate erased data.

DATA-004
Custom fields.

Operators define custom fields in settings on each entity listed in seed.json → customFields.entities, and can read and edit their values wherever that entity is shown. A custom field is operator-only and is never projected to a public surface.


Baseline guarantees

22 declared clauses from this product's BASELINE.md. Selected ownership guarantees are shown below; execution evidence is separate.

BASE-DATA-005
Independence.

The deployment makes no request to the marketplace or the seed author. There is no telemetry, phone-home, licence check, or kill switch.

Declared guarantee
BASE-DATA-001
Export and import.

pnpm export produces every record and attachment; pnpm import restores it into an empty deployment; export, import, export yields an archive with equivalent contents.

Declared guarantee
BASE-DATA-004
Outbound flows.

User data leaves the deployment only through externals declared in seed.json → externals. No other outbound request carries user data.

Declared guarantee
BASE-PUBLIC-001
Privacy.

Public surfaces (widgets, booking pages, forms, status pages) set no cookies, load nothing from third-party domains, and send no visitor data before the visitor's first interaction.

Declared guarantee
BASE-SECRET-001
No leakage.

No secret appears in the client bundle, in logs, or in an error response.

Declared guarantee

BASELINE.md for Penny


Release history

Honest version: there is one release line so far, and we will not invent the rest.

v0.0.0-bootstrap
Current — ui-port-read-spine

The manifest states this version and maturity. Consult the source verification evidence before deployment.


DIY quickstart

The code is MIT and the deployment is yours. Nothing below talks to us; optional management remains planned.

Use the edition's source README for its available installation commands and required configuration.


View as agent

Everything on this page exists as machine-readable files. If you are an agent evaluating Penny for someone, read these instead of the prose.

Installation instructions are in the source README; this entry does not declare a catalogue install command.

Catalogue endpoint

/agents/catalogue.json

Every seed with its id, category, version, clause count, install command and links to the files on the left. Generated from the seeds' own artifacts on every build, so it cannot drift from what ships.

The machine face

Penny landing screenshot

Run Penny on your own cloud.

MIT code, your data, your infrastructure. Leave whenever you like and it keeps running.