ADRs
ADR 0082 — Per-project repo hosting under Arno's org + monetized developer-share (reseller model)
  • Date: 2026-08-06
  • Status: Proposed (design spec — architecture-first; no code, gated on maintainer-provisioned infra)
  • Phase / Feature: turning the "give the developer a git link" action into an invisible, Arno-hosted, paid share
  • Builds on: ADR 0081 (per-project repo, §6 + its 2026-08-06 amendment — Variant A, invisible provisioning, lazy-create-at-share), ADR 0049 / 0052 (Sorgente + prod model)
  • Ratified: the maintainer chose Variant A (repos under Arno's org, one-time maintainer setup, zero per-user activity) and directed that payment route through Arno (reseller markup), gated at the share action. Billing model recommended as subscription; final price is a business decision left to the maintainer.

Context

ADR 0081 makes each project's divergence materialize as a git repo, provisioned lazily at first share under Arno's own GitHub org (Variant A). This ADR records the hosting-at-scale and monetization decisions that ride on that share action: who pays whom, how cards are handled, and what the customer buys. ADR 0081 owns the git model; this ADR owns the money and the account topology.

Driving scenario (maintainer's words): a designer finishes a design, wants to hand a developer a link — at that moment: pay; OK, pays by card; link issued.

Decision

§1 — Reseller model: Arno is the merchant, GitHub is a cost input

Arno holds a single GitHub Enterprise account for its org and pays GitHub. The user pays Arno, not GitHub, and never holds a GitHub account or bill. Standard reseller/markup arrangement: GitHub cost is an input, Arno sets the customer price. Because Variant A read-links consume no GitHub seat (ADR 0081 amendment, Cost), the marginal GitHub cost of an issued read-link is ≈ $0 → the share price is almost entirely margin. The only real cost input is an opt-in write-collaborator seat, priced as a higher tier or passed through.

§2 — The share action is the paywall + lazy-create gate

The owner clicks "give the developer a link." Flow:

  1. Paywall interstitial (price from the billing model, §4).
  2. Card handled by a PCI-compliant processor (§3) — Arno's UI and servers never receive card data.
  3. On payment.succeeded webhook → backend lazily creates the Arno-org private repo (ADR 0081 §6), commits the current resolved export, and issues the read-token clone-link.

Payment and repo creation coincide — no repo is created unpaid. This is why lazy-create (ADR 0081 amendment C) and monetization share one gate: the same event that earns revenue is the one that incurs the (near-zero) GitHub cost.

§3 — Card handling: processor-only, never Arno (safety-critical)

Card data flows only through a PCI-compliant processor — Stripe Checkout or a Merchant-of-Record (Paddle / Lemon Squeezy). Arno stores no PAN, renders no raw card field; it receives only processor tokens + webhooks.

Recommendation: a Merchant-of-Record (Paddle / Lemon Squeezy). At 100k global users the MoR also assumes VAT / sales-tax liability and remittance, which raw Stripe leaves to Arno. Trade-off: an MoR takes a higher cut than Stripe's base rate in exchange for offloading global tax compliance.

§4 — Billing model: subscription (recommended) vs one-time unlock

The share buys access to a living repo — under the hood Arno keeps syncing the designer's edits into it (ADR 0081 invisible model). Two models:

  • Subscription (recommended). The link/repo stays live and auto-synced while the subscription is active. Matches the ongoing service actually rendered (continuous sync ≠ one-time dump); recurring revenue; a lapsed subscription revokes the token and pauses sync (§5).
  • One-time unlock. Simpler UX ("pay once, link issued"), but a single payment against a perpetually-synced repo does not cover ongoing cost — economically unbalanced.

Ratified direction: subscription, with the final price a business decision the maintainer sets (not fixed here). Write-collaborator seats (§1/§5) are a higher tier or add-on.

§5 — Access tiers map to cost

TierMechanismGitHub seatCustomer price
Consumer (read)tokenized clone-linknone — $0base share price (≈ pure margin)
Contributor (write)collaborator by GitHub handle or deploy keycollaborator = 1 paid seat; deploy key = $0higher tier / passthrough

Token links are revocable / rotatable from Arno: revoke = invalidate the token + pause sync. This is the enforcement point behind a lapsed subscription (§4).

Anti-patterns

  • Do not let Arno's servers or UI touch raw card data — processor-only (§3). This is also a hard platform rule: the agent never enters card/financial credentials nor creates payment accounts; the maintainer wires the processor with their own keys via secrets.
  • Do not create a GitHub repo before payment — creation is the post-payment step (§2), keeping unpaid repos (and GitHub cost) at zero.
  • Do not give every project a repo at create — that reintroduces 100k unpaid repos (ADR 0081 amendment C).
  • Do not hand a write-collaborator seat by default — it is the one paid GitHub input; default to read-token-links (§5).
  • Do not price a perpetually-synced repo as a one-time fee (§4).
  • Do not expose the user to GitHub at all in Variant A — no GitHub account, login, or bill reaches the Arno user; GitHub is Arno's internal cost input (§1).

Infra gates (maintainer-provisioned — credentials, not code)

  1. GitHub org + Enterprise agreement sized for automated bulk repo creation (pre-clear the pattern with GitHub — automated mass repo creation under one org is atypical and can trip abuse limits).
  2. GitHub App (scopes: repo-create, contents-write, collaborator-add) — secrets provisioned on arno-api-test (today absent → blocks all git writes, per ADR 0081).
  3. Payment processor account — Stripe or (recommended) a Merchant-of-Record (Paddle / Lemon Squeezy); API keys via secrets; a webhook endpoint for payment.succeeded / subscription lifecycle.

All three are credential/account steps only the maintainer can perform. The agent does not create payment accounts or enter card/financial credentials.

Roadmap

  • P1 — DONE (2026-08-07). GitHub App + Arno-org provisioning path, proven end-to-end on the test stand. apps/api/src/projects-app/: provision.ts (mint token via PROJECTS_APP_* + idempotent ensureOrgRepo) and export.ts (provisionAndExportProject — read pins → serializeDesignExport → commit design/* straight to the repo default branch, no PR). Maintainer-gated routes POST /api/v1/projects-app/smoke + POST /api/v1/projects/:projectId/org-export. Verified live: private repo created under arno-projects, three design/* files committed, and a re-export of an unchanged pin layer is a git no-op (deterministic manifest — serialize omitExportedAt). Infra provisioned by the maintainer: org arno-projects, App Arno Project Repos (id 4513994), PROJECTS_APP_* secrets on arno-api-test.
  • P2 — payment processor integration (Checkout / MoR) + payment.succeeded → the same provisionAndExportProject seam behind the paywall + issue-token-link. Blocked on the payment stack (Block 3 of the infra runbook).
  • P3 — subscription lifecycle (active → synced; lapsed → revoke token + pause sync); write-collaborator tier. Auto-sync-on-edit needs a per-project "provisioned" marker so a pin change re-exports ONLY after the repo exists (lazy-create preserved) — a small schema delta, deferred with the other §8 deltas.
  • P4 — token rotation / revocation UI; deploy-key path for seat-free write.

References

  • ADR 0081 — per-project repo (§6) + 2026-08-06 amendment (Variant A, invisible provisioning, lazy-create-at-share). This ADR is the billing/hosting half of that amendment.
  • ADR 0049 / 0052 — Sorgente + prod model (why prod has no live Sorgente DB; the release channel projects read through).
  • Platform rule — the agent never enters card / financial credentials or creates payment accounts; the maintainer wires the processor.