Sandbox The hosted app is connected to PayPal's sandbox: no real money moves

Security

Security at Milestone Ledger

Milestone Ledger decides when other people's money moves and holds the work they deliver. This page says how it is built to be trusted with that, what is not there yet, and how to tell us when something is wrong.

Last reviewed

Money

  • PayPal holds the funds, not us. Funding a tranche creates a PayPal authorization that the payer approves on PayPal's own pages. We never see card or bank details.
  • In live mode the platform never holds funds. Payment goes from the payer to the payee's connected PayPal account. The platform route, where our account receives and pays out, exists for the sandbox only and is refused by a live deployment.
  • Captures are reconciled. An hourly job asks PayPal about every capture, payment and invoice still in flight, so a missed webhook never strands a tranche, and polls PayPal's Disputes API for disputes no webhook announced before paying anything out.
  • Incoming PayPal webhooks are verified against the webhook id with PayPal's signature verification, and rejected without it.
  • Outgoing webhooks are signed. Every event we send to your systems carries an HMAC signature. The Python and TypeScript SDKs include the verification helpers.

Who can do what

  • Everything happens as a signed-in person acting for an organization, with a role: owner, admin, member or viewer. The permission table is pinned by tests to the specification.
  • Human-only steps. Confirming criteria, funding, approving or rejecting a tranche, releasing, contesting and disputing can be done only by a person with the right role. They are never offered to an MCP client or an API key, even one that calls the REST API directly with its token.
  • Each side sees what its role allows. A payer never sees withheld work before capture; a reviewer sees what it needs to decide; the counterparty's members see their own side.
  • Sign-in is by emailed magic link, or by your organization's OpenID Connect identity provider for domains you have verified by DNS. Sign-in and invitation tokens are removed from the address bar as soon as they are read. The API's own sign-in pages pair the token with a short-lived SameSite cookie so a cross-site request cannot sign someone's browser in as somebody else.
  • Agents connect over OAuth with a consent page where a person signs in and picks the organization the agent acts for. Headless agents use organization API keys, which are stored hashed and can be revoked.

Custody of evidence and deliverables

  • Encrypted at rest. Uploaded files are encrypted with AES-256-GCM before they are stored, whether in the database or in an object store. The object store only ever holds ciphertext.
  • Downloads are checked. Every download is served by the application after an access check, is written to the audit log, and is refused if its SHA-256 hash does not match what was stored. There are no signed bucket URLs.
  • Handover is verified. When a contractor's code is handed to the client at capture, the git tree is recomputed from the source and must equal the verified commit's tree, or the handover fails closed.
  • Retention is a setting, not an accident. Each deployment chooses how long closed tranches' originals are kept, and nothing is removed within 180 days of a capture, PayPal's chargeback window. Removal keeps the hashes and is recorded in the audit log.
  • Evidence is rendered as text, never as HTML, in the web app.

Verification integrity

  • Deterministic checks run before the model and their result is final.
  • The verifier must cite the evidence for every criterion it marks met, and the server derives the verdict from the structured result, not from free text.
  • A skeptic pass argues the opposite case before any auto-release, and disagreement sends the tranche to a person.
  • Each verdict records the criteria hash, the evidence ids, and the model and prompt version, so any decision can be replayed.
  • Prompt injection through evidence is in scope for our security programme (see below). Evidence is untrusted input to the verifier, and the human gate and the contest window exist because no verifier is perfect.

The platform

  • The web app loads no third-party scripts or fonts, and this site loads none either. The API's own pages (sign-in, consent, pay links) are served with a content security policy that allows no scripts and no outside resources.
  • Session tokens expire after 30 days, sign-in links after 15 minutes and invitations after 14 days. Only hashes of tokens are stored.
  • Sign-in links, extraction, pre-check and pay-link visits are rate limited per client.
  • Secrets such as organizations' SSO client secrets are encrypted at rest.
  • Every state transition appends an immutable event to the audit log. The log and the compliance packet are available to the organizations party to an agreement.
  • The hosted service runs on Render, with Postgres on Render and encrypted file storage on Cloudflare R2, in the United States.

What is not there yet

We would rather say this here than have you find out later.

  • The hosted service runs in PayPal's sandbox. Live payments need PayPal partner approval, which we are applying for.
  • There is no SOC 2 report yet. Reconciliation and a tamper-evident audit log come first, then the policies, then a Type I and a Type II.
  • Screen-reader passes and some other manual checks are still in progress; see the accessibility statement.
  • Open work is tracked in the repository's issues.

Reporting a vulnerability

Please report problems privately so we can fix them before they are public.

  • Use GitHub's private reporting: Security → Report a vulnerability on the repository, or email security@milestone-ledger.com.
  • Include what you found, how to reproduce it (requests, accounts or agreement setup), and what an attacker gains.
  • We aim to acknowledge reports within 3 business days and to tell you what we will do within 10. We will credit you in the fix unless you would rather we did not.
  • Test against your own instance in sandbox mode, or the development server in the repository. Please do not test against app.milestone-ledger.com or other people's data, and do not run load or denial-of-service tests against it.

In scope

  • The engine: REST API, MCP server and OAuth, magic-link sign-in, organization SSO, API keys.
  • Money paths: funding, holds, capture, release, refunds, Payouts, invoice mode and pay links, PayPal webhooks.
  • Access between organizations and roles: anything that lets one party see another party's evidence or take a step that belongs to someone else, or lets an agent take a human-only step.
  • Verification integrity: getting a tranche marked verified or released without meeting its criteria, including prompt injection through evidence.
  • The web app and outbound webhooks (SSRF, signature bypass).

Out of scope

  • PayPal's, Anthropic's, GitHub's, Resend's or Render's own services. Please report those to them.
  • Findings that need a compromised device, browser or organization admin account.
  • Missing best-practice headers or rate limits with no demonstrated impact.
  • The test stand-ins in the repository, which never run on a deployment.

The full policy is the repository's SECURITY.md. Only the latest commit on main and the hosted deployment it runs are supported.