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




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.
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
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
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
With no release published, the public updates surface states that there are no updates yet and renders no draft, example, or placeholder entry.
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.
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.
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
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.
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.
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.
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.
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
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.
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.
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.
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.
The deployment makes no request to the marketplace or the seed author. There is no telemetry, phone-home, licence check, or kill switch.
pnpm export produces every record and attachment; pnpm import restores it into an empty deployment; export, import, export yields an archive with equivalent contents.
User data leaves the deployment only through externals declared in seed.json → externals. No other outbound request carries user data.
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.
No secret appears in the client bundle, in logs, or in an error response.
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.
- README.md
- Source setup, available commands and implementation status.
- seed.json
- The manifest: capabilities mapped to clauses, externals, extension points, limits, operating cost, deploy requirements.
- CONTRACT.md
- The 85 clauses above, in source form.
- BASELINE.md
- The product's declared baseline requirements.
- AGENTS.md
- Instructions written for the coding agent that will maintain this deployment.
- /llms.txt
- The site, for machines.
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.

Run Penny on your own cloud.
MIT code, your data, your infrastructure. Leave whenever you like and it keeps running.