From 3e77403aea9fa9c089afb80b6c9da9135331312c Mon Sep 17 00:00:00 2001 From: solomon2773 Date: Thu, 27 Aug 2026 15:40:47 -0500 Subject: [PATCH 1/2] docs: add codebase guide and contributor context --- .env.example | 5 + AGENTS.md | 96 ++++ ARCHITECTURE.md | 53 +- CLAUDE.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 784 ++++++++++++++++++++++------ docs/getting-started/environment.md | 8 +- docs/roadmap.md | 23 +- prisma/AGENTS.md | 52 ++ src/app/AGENTS.md | 37 ++ src/lib/extensions/AGENTS.md | 45 ++ 11 files changed, 929 insertions(+), 178 deletions(-) create mode 100644 AGENTS.md create mode 100644 prisma/AGENTS.md create mode 100644 src/app/AGENTS.md create mode 100644 src/lib/extensions/AGENTS.md diff --git a/.env.example b/.env.example index e0b99c9..ae77f1f 100644 --- a/.env.example +++ b/.env.example @@ -15,8 +15,13 @@ DIRECT_URL="postgresql://openhosting:openhosting@localhost:5432/openhosting" CRON_SECRET="change-me" # Optional: initial admin password used by `npm run db:seed` +# SEED_ADMIN_EMAIL="admin@example.com" # SEED_ADMIN_PASSWORD="change-me" +# Optional: public origin applied by the seed on a fresh install. After setup, +# manage this under Admin → Settings → Public URL. +# APP_URL="http://localhost:3000" + # ── Ticket attachment storage ─────────────────────────────────────────────── # Default: keep attachment bytes in PostgreSQL. Set to "s3" for AWS S3, # Cloudflare R2, MinIO, Backblaze B2, or another S3-compatible private bucket. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..20afded --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,96 @@ +# OpenHosting repository guidance + +## Project summary + +OpenHosting is a modular Next.js 16 App Router application for hosting billing, +customer management, support, and infrastructure provisioning. PostgreSQL is +accessed through Prisma 7. Read `README.md` for setup and repository orientation +and `ARCHITECTURE.md` for the core flow before changing cross-domain behavior. + +More specific guidance exists in: + +- `src/app/AGENTS.md` for pages, layouts, Server Actions, and route handlers. +- `src/lib/extensions/AGENTS.md` for third-party drivers. +- `prisma/AGENTS.md` for schema, migration, and seed work. + +## Commands + +```bash +npm ci +npm run db:generate +npm test +npm run test:integration +npm run typecheck +npm run build +``` + +Local development additionally needs PostgreSQL and normally uses: + +```bash +cp .env.example .env +npm run db:push +npm run db:seed +npm run dev +``` + +Unit tests use Vitest. Database integration tests use a disposable PostgreSQL +Testcontainer and apply the checked-in migrations before the test files run; +they therefore require a working Docker daemon. CI requires both suites, Prisma +generation, strict type checking, a production Next.js build, and a Docker +build. Exercise third-party integrations manually against provider sandboxes. + +## Architectural boundaries + +- `src/app` contains Server Components and route handlers. UI mutations live in + domain modules under `src/lib/actions`. +- `src/lib/services` owns reusable domain workflows. Keep pricing in + `services/orders.ts`, payment integration in `services/payments.ts`, server + integration in `services/provisioning.ts`, resale integration in + `services/resale.ts`, and AI support policy in `services/ai.ts`. +- Keep delegated-spend and idempotency policy in `agent-commerce-policy.ts`, + checkout orchestration in `services/agent-checkout.ts`, protocol adaptation + in `services/acp.ts` / `x402.ts`, and processor calls in gateway drivers. +- `src/lib/billing.ts` owns invoice and recurring-service state transitions. It + must call service abstractions instead of concrete drivers. +- Concrete third-party code belongs under `src/lib/extensions` and is registered + in `src/lib/extensions/registry.ts`. +- All database access goes through `db` from `src/lib/db.ts`. +- Import Prisma types/client code from `@/generated/prisma/client`, never from + `@prisma/client`; generation output is intentionally git-ignored. + +## Authentication, authorization, and audit + +- Use `requireUser()` for authenticated customer operations and + `requireAdmin(permission)` for staff operations. Perform checks inside every + mutation, even when the corresponding UI is hidden. +- REST handlers use `withApiKey(scope, handler)` and should validate write + payloads with Zod. +- Add `audit()` calls for meaningful auth, admin, billing, account, and service + lifecycle mutations. Never include passwords, tokens, driver credentials, or + full payment details in audit metadata or logs. +- Webhook signature verification belongs in the gateway driver. +- Agent checkout requires a customer-owned `ag_` grant, an exact allowlist, + bounded base-currency spend, expiry, and idempotency. Never weaken one of + those checks in a protocol-specific route. + +## UI and settings + +- All app routes are deliberately dynamic; the production build must not need + a live database. +- Existing localized UI uses `getT()` and keys from `src/lib/i18n.ts`. English + defines the key union, so add every required locale entry with a new key. +- Theme tokens are registered in `src/lib/themes.ts` and implemented in + `src/app/globals.css`. +- Runtime behavior belongs in the `Setting` table with a safe default in + `src/lib/settings.ts`; deployment-only secrets and connectivity belong in + environment variables. + +## Documentation and deployment + +- Update the relevant page under `docs/` when operator-visible behavior changes. +- Environment/startup changes may require synchronized edits to `.env.example`, + `Dockerfile`, `docker-entrypoint.sh`, `docker-compose.yml`, `install.sh`, and + `deploy/k8s`. +- Refresh README screenshots with `npx tsx scripts/screenshots.ts` when changing + a pictured UI. The script expects a running app with seeded data. +- Preserve unrelated working-tree changes and do not edit generated artifacts. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f4f19f2..b894d3d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Architecture -OpenHosting is a single Next.js 15 (App Router) application backed by +OpenHosting is a single Next.js 16 (App Router) application backed by PostgreSQL through Prisma. Server Components render the UI, Server Actions handle mutations, and route handlers expose the REST API, webhooks and the billing cron endpoint. @@ -16,18 +16,24 @@ src/ │ ├── admin/ staff panel: billing, catalog, customers, system │ └── api/ │ ├── v1/ REST API (API-key auth) +│ ├── agent/ delegated catalog and native checkout +│ ├── acp/ versioned Agentic Commerce Protocol adapter +│ ├── mcp/ remote Streamable HTTP MCP transport +│ ├── x402/ exact USDC invoice settlement │ ├── webhooks/ payment gateway callbacks │ └── cron/ recurring billing tick ├── lib/ │ ├── auth.ts sessions, password hashing, RBAC guards │ ├── billing.ts invoice lifecycle + renewal/suspension engine +│ ├── agent-commerce-policy.ts delegated spend/idempotency primitives +│ ├── x402.ts protocol encoding + facilitator boundary │ ├── cart.ts cookie cart storage │ ├── mail.ts SMTP delivery + templated emails │ ├── settings.ts key-value settings with defaults │ ├── totp.ts RFC-6238 TOTP (no external dependency) │ ├── actions/ Server Actions, one module per domain │ ├── services/ orders, payments, provisioning services -│ └── extensions/ gateway & server drivers + registry +│ └── extensions/ gateway, server, resale & AI drivers + registry └── components/ shared UI (forms, badges, ticket thread) prisma/ schema, migrations, seed deploy/k8s/ Kubernetes manifests @@ -44,36 +50,55 @@ deploy/k8s/ Kubernetes manifests that implements the driver interface and one registry entry — no changes to checkout, billing or the admin UI, which all render driver config forms from the driver's own `configFields` metadata. -- **Liskov substitution.** Every gateway satisfies `GatewayDriver` and every - provisioning backend satisfies `ServerDriver`; the engine treats them - uniformly and any driver can replace any other. -- **Interface segregation.** Payment and provisioning are separate interfaces; - optional capabilities (webhooks) are optional methods, so a simple offline - gateway like bank transfer implements only what it needs. +- **Liskov substitution.** Gateways, provisioning backends, resale providers, + and AI providers each satisfy their own driver contract; their service + boundary treats implementations of the same contract uniformly. +- **Interface segregation.** Payment, provisioning, resale, and AI use separate + interfaces. Optional capabilities such as gateway webhooks/off-session + charging, resale renewal, and constrained AI output are optional methods. - **Dependency inversion.** High-level policy depends on abstractions: - `billing.ts` calls `services/provisioning.ts`, and checkout calls - `services/payments.ts`. Only those two service modules resolve concrete - drivers. + `billing.ts` delegates provider work to `services/provisioning.ts` and + `services/resale.ts`, while checkout and webhooks call + `services/payments.ts`. AI support policy lives in `services/ai.ts`; these + service modules, rather than pages or billing policy, resolve drivers. ## Billing lifecycle -``` +```text checkout ──► Order + pending Services + Invoice payment (gateway webhook / credits / admin) ──► markInvoicePaid() ├─ first payment: Service ACTIVE + driver.create() └─ renewal: expiresAt += cycle (+ unsuspend if needed) hourly cron (/api/cron, Bearer CRON_SECRET) - ├─ generateRenewalInvoices() N days before expiry + ├─ generateRenewalInvoices() N days before expiry (+ metered usage) + ├─ autoChargeDueInvoices() supported stored payment method + ├─ cancelEndOfTermServices() customer-scheduled cancellation ├─ suspendOverdueServices() grace days after expiry → driver.suspend() └─ cancelStaleSuspendedServices() after suspension window → driver.terminate() ``` +## Agent commerce + +```text +customer ──► expiring AgentGrant (products + currencies + spend limits) +agent ──► public catalog ──► native checkout / ACP checkout session + │ + ├─ serializable spend reservation ──► Order + Invoice + ├─ Stripe SPT (fiat) ──► PaymentIntent ──► markInvoicePaid() + └─ x402 v2 (USDC) ──► verify + settle ──► markInvoicePaid() +``` + +`services/agent-checkout.ts` is the canonical order boundary. ACP adapts its +session lifecycle to that service; x402 adapts signed payment terms to the +normal invoice-payment lifecycle. Protocol routes do not calculate prices or +provision services themselves. + ## Security notes - Sessions are opaque IDs stored server-side with a 14-day TTL; cookies are `httpOnly` + `SameSite=Lax`. -- Passwords use bcrypt; API keys and one-time tokens are stored as SHA-256 +- Passwords use bcrypt; API keys, agent grants, and one-time tokens are stored as SHA-256 hashes only. - TOTP 2FA uses a ±1 window and constant-time comparison. - RBAC: users with a `Role` are staff; permissions are checked per admin area diff --git a/CLAUDE.md b/CLAUDE.md index d1758fd..442a61f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -OpenHosting is an open-source billing and client-management platform for hosting providers: a single Next.js (App Router) app backed by PostgreSQL through Prisma 7. See ARCHITECTURE.md for the full design; the essentials are below. +OpenHosting is an open-source billing and client-management platform for hosting providers: a single Next.js 16 (App Router) app backed by PostgreSQL through Prisma 7. See ARCHITECTURE.md for the full design; the essentials are below. ## Commands diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62de206..f1eb49a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,7 @@ Logins after seeding: `admin@example.com` / `admin12345` and ## Good first contributions - New payment gateways (Mollie, Coinbase Commerce, Razorpay, …) — see - [docs/extensions.md](docs/extensions.md), usually < 100 lines + [Writing an extension](docs/extensions/writing-extensions.md), usually < 100 lines - New server integrations (Proxmox, VirtFusion, cPanel/WHM, Plesk, CyberPanel) - Translations / i18n groundwork - Tests around the billing engine diff --git a/README.md b/README.md index f66e31f..286f652 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ # 🚀 OpenHosting -**Open-source billing & client management for hosting providers.** -Storefront, recurring billing, provisioning and support — one modern stack: Next.js 16 · Prisma · PostgreSQL/Supabase · Docker · Kubernetes. +**Open-source billing and client management for hosting providers.**
+Storefront, recurring billing, provisioning, product resale, and support in one +self-hosted application. [![CI](https://img.shields.io/badge/CI-GitHub_Actions-2088FF?logo=githubactions&logoColor=white)](.github/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Next.js 15](https://img.shields.io/badge/Next.js-15-black?logo=next.js)](https://nextjs.org) +[![Next.js 16](https://img.shields.io/badge/Next.js-16-black?logo=next.js)](https://nextjs.org) [![Prisma](https://img.shields.io/badge/Prisma-PostgreSQL-2D3748?logo=prisma)](https://www.prisma.io) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) @@ -15,188 +16,673 @@ Storefront, recurring billing, provisioning and support — one modern stack: Ne ---- - -## Why OpenHosting? - -Selling game servers, VPS or web hosting means gluing together a store, recurring -billing, provisioning and support. OpenHosting delivers that entire workflow as a -single modern TypeScript app you can deploy anywhere a container runs — a VPS, -any cloud, or a Kubernetes cluster — with Postgres or Supabase as the only -dependency. No license fees, no legacy stack. - -## ✨ Features - -**Store & checkout** -- Product catalog with categories, stock control and visibility rules -- Six billing cycles (one-time → biennially) with setup fees per cycle -- Configurable options (RAM, disk, …) with per-option pricing that scales with the cycle -- Cart, coupons (percent/fixed, per-product restrictions, limited uses, expiry) and country-aware tax rates -- **Multi-currency**: exchange rates, storefront currency picker, orders locked to their currency - -**Billing engine** -- Orders → invoices → services, fully automated -- Automatic renewal invoicing, overdue suspension and termination (cron-driven) -- 19 payment gateways: cards (Stripe, PayPal, Mollie, Square, Authorize.net, Braintree), crypto (Coinbase Commerce, NOWPayments, BTCPay, CoinGate), SEPA (GoCardless), merchant-of-record (Lemon Squeezy), and regional (Razorpay, Mercado Pago/PIX, Paystack, Flutterwave, Midtrans, Xendit) -- Account credit balance, zero-total auto-activation, manual admin payments - -**Provisioning** -- 27 server integrations: game panels (Pterodactyl, Pelican, WISP, TCAdmin2), VPS/cloud (Proxmox, SolusVM, Convoy, VirtFusion, Virtualizor, Hetzner, DigitalOcean, Vultr, Linode), enterprise virt (OpenStack, OnApp, Virtuozzo, VMware vCloud) and web panels (cPanel/WHM, DirectAdmin, Plesk, Enhance, HestiaCP, CyberPanel, CWP, InterWorx, ISPConfig, Webmin/Virtualmin) -- Automatic create / suspend / unsuspend / terminate on billing events -- Config options map to environment variables on the provisioned server - -**Product resale** (beyond servers) -- Domain registrars: Enom, ResellerClub, Namecheap, OpenSRS, Openprovider (register/renew) -- SSL certificates (GoGetSSL), software licenses (cPanel/LiteSpeed/Softaculous/CloudLinux), and Microsoft 365 / Google Workspace seat resale -- Customers enter the required details (domain, CSR, seat count) at checkout - -**Client area** -- Dashboard, services, invoices with online payment, account credit -- Support tickets with departments, priorities and email notifications -- Profile, password management and **TOTP two-factor auth** with QR enrolment - -**Fraud prevention** -- Order review queue (hold risky orders before provisioning), ban lists (email/domain/IP/country), disposable-email blocking -- Velocity limits, MaxMind minFraud & FraudLabs Pro scoring, checkout captcha, EU VAT (VIES) reverse charge - -**Affiliate program** -- Referral links, per-product or default commissions, one-time or recurring, payout threshold, affiliate & admin dashboards - -**Admin panel** -- Revenue dashboard, orders, invoices (mark paid / cancel), service lifecycle controls -- Product, category, coupon, tax, user and role management (RBAC permissions) -- Extension configuration UI, editable email templates, SMTP settings -- Full audit log and searchable customer list - -**Platform** -- **6 built-in themes** switchable at runtime, plus a two-file recipe for custom ones -- **Multi-language UI** (English, Dutch, French, German, Spanish) with a storefront language picker -- REST API (`/api/v1`) with scoped API keys, plus a **CLI** (`oh`) and an **MCP server** for AI assistants -- One-command importers to migrate from other billing panels -- Email notifications via SMTP with templated, per-event messages -- Docker image, docker-compose stack, Kubernetes manifests with HPA + CronJob - -## 📸 Screenshots - -| Storefront | Product configurator | Cart | +## Contents + +- [What OpenHosting includes](#what-openhosting-includes) +- [Technology stack](#technology-stack) +- [How the application is structured](#how-the-application-is-structured) +- [Core business flows](#core-business-flows) +- [Repository map](#repository-map) +- [Local development](#local-development) +- [Configuration](#configuration) +- [Commands](#commands) +- [APIs and automation](#apis-and-automation) +- [Extension system](#extension-system) +- [Database and data model](#database-and-data-model) +- [Security model](#security-model) +- [Testing and continuous integration](#testing-and-continuous-integration) +- [Deployment](#deployment) +- [Development conventions](#development-conventions) +- [Documentation](#documentation) + +## What is OpenHosting? + +Hosting businesses usually have to connect a catalog, checkout, recurring +billing, infrastructure provisioning, customer accounts, and support tooling. +OpenHosting keeps those concerns in one TypeScript application backed by +PostgreSQL. It can sell game servers, VPS and web hosting, as well as domains, +SSL certificates, software licenses, and productivity-suite seats. + +The application has four primary surfaces: + +1. A public storefront and knowledgebase. +2. An authenticated client area for services, invoices, tickets, quotes, and + account management. +3. A permission-guarded administration panel. +4. REST, OAuth2, CLI, and MCP interfaces for external automation. + +OpenHosting is self-hosted, has no license server, and ships as a standalone +Next.js container with Docker Compose and Kubernetes deployment examples. + +## What OpenHosting includes + +### Storefront and catalog + +- Categories, products, visibility, optional sold-out status, and quantities. +- One-time, monthly, quarterly, semi-annual, annual, and biennial pricing. +- Per-cycle setup fees and priced configuration options such as RAM or disk. +- Cookie-backed cart, coupons, country-specific tax rates, and EU VAT reverse + charge support. +- Multiple currencies with configurable exchange rates; orders and services + retain the currency used at checkout. +- Public announcements/blog posts and a searchable knowledgebase. + +### Orders and billing + +- Checkout creates an order, invoice, invoice items, and pending services in a + database transaction. +- Payment by account credits, a hosted gateway, an administrator, or a + zero-total order. +- Automatic renewal invoices, metered usage line items, stored-payment + auto-charge, suspension, unsuspension, and termination. +- Quotes that customers can accept into invoices and product upgrade paths. +- Configurable cancellation at once or at the end of the paid period. + +### Integrations + +- **20 payment gateways**, including Stripe, x402/USDC, PayPal, Mollie, Square, + Authorize.net, Braintree, GoCardless, crypto processors, and regional + providers. +- **27 server drivers** for game panels, VPS/cloud platforms, enterprise + virtualization, and web-hosting control panels. +- **9 resale drivers** for domain registrars, SSL certificates, software + licenses, Microsoft 365, and Google Workspace. +- An AI-provider driver for staff-reviewed support reply drafts, ticket triage, + source/confidence-gated tier-1 resolution, and a read-only customer assistant. + Every AI feature is disabled by default and independently opt-in. + +All integrations are disabled until configured, except the demonstration bank +transfer method created by the development seed. + +### Customers, support, and administration + +- Customer dashboard, services, invoices, quotes, notifications, credits, + billing methods, affiliates, and profile management. +- Support tickets with departments, priorities, assignment, email/in-app + notifications, and database-backed attachments. +- Customer-managed additional contacts with notification and permission + metadata. +- Registration, email verification, password reset, TOTP two-factor + authentication, login throttling, and optional mandatory staff 2FA. +- Order fraud review, ban lists, disposable-email detection, velocity rules, + captcha, MaxMind minFraud, and FraudLabs Pro. +- Roles and permissions, audit logs, editable email templates, SMTP delivery, + mass mail, currencies, themes, and localization. + +### Platform and automation + +- REST API with scoped API keys. +- OAuth2 authorization-code provider and user-info endpoint. +- Delegated agent purchasing with a machine-readable catalog, idempotent + checkout, product/currency allowlists, and spend-capped customer tokens. +- ACP 2026-04-17 checkout sessions through Stripe Shared Payment Tokens and + x402 v2 exact USDC settlement for autonomous payments. +- Zero-dependency Node.js CLI and an MCP server exposing the same management + operations to AI assistants. +- WHMCS and Paymenter import scripts. +- Six runtime-selectable themes and five included locales: English, Dutch, + French, German, and Spanish. + +## Technology stack + +| Layer | Technology | Notes | |---|---|---| -| ![Storefront](docs/screenshots/storefront-home.png) | ![Product](docs/screenshots/storefront-product.png) | ![Cart](docs/screenshots/storefront-cart.png) | +| Web application | Next.js 16 App Router, React 19 | Server Components render reads; Server Actions handle UI mutations | +| Language | TypeScript 5.9 | Strict mode, `@/*` mapped to `src/*` | +| Styling | Tailwind CSS 4 | Global component/theme rules live in `src/app/globals.css` | +| Database | PostgreSQL 14+ | PostgreSQL 18 is used by the provided Compose stack | +| Data access | Prisma 7 with `@prisma/adapter-pg` | Generated client is written to `src/generated/prisma` | +| Validation | Zod 4 | Primarily used at API and form boundaries | +| Authentication | Server-side sessions, bcrypt, Node crypto | Opaque 14-day sessions; TOTP is implemented locally | +| Email | Nodemailer | SMTP settings and templates are stored in PostgreSQL | +| Integrations | Driver interfaces and native `fetch` | Payment, provisioning, resale, and AI adapters | +| Tooling | Node.js 24, npm, Playwright | Playwright currently generates documentation screenshots | +| Deployment | Standalone Next.js output, Docker, Kubernetes | Runtime container applies migrations unless configured otherwise | + +## How the application is structured + +OpenHosting is a modular monolith. UI, API, billing policy, and integration +drivers deploy together, while the code keeps domain orchestration separate +from third-party protocols. + +```text +Browser / API client / CLI / MCP client + │ + ▼ +Next.js App Router +├── Server Components ─────────────── read models and render HTML +├── Server Actions ────────────────── authenticated UI mutations +└── Route Handlers ────────────────── REST, OAuth, webhooks, cron, files + │ + ▼ +src/lib +├── services/ ─────────────────────── domain workflows and integration ports +├── billing.ts ────────────────────── invoice and recurring-service policy +├── auth.ts / api-auth.ts ─────────── session, RBAC, and API-key guards +└── extensions/ ───────────────────── gateway/server/resale/AI drivers + │ + ▼ +Prisma client ───────── PostgreSQL / Supabase +``` -| Client dashboard | Invoice payment | Support ticket | -|---|---|---| -| ![Dashboard](docs/screenshots/client-dashboard.png) | ![Invoice](docs/screenshots/client-invoice-pay.png) | ![Ticket](docs/screenshots/client-ticket.png) | +Every page is dynamically rendered because it depends on live settings, +catalog data, or session state. `next build` therefore does not need a running +database. Mutations are grouped by business domain under `src/lib/actions`, +while reusable business operations live under `src/lib/services`. + +Important dependency boundaries: + +- UI code and route handlers call actions or services; they do not call a + concrete payment or provisioning driver. +- `src/lib/services/payments.ts` is the payment-driver boundary. +- `src/lib/services/provisioning.ts` is the server-driver boundary. +- `src/lib/services/resale.ts` is the resale-driver boundary. +- `src/lib/services/ai.ts` owns AI support policy and resolves the active AI + provider. +- `src/lib/billing.ts` owns invoice state transitions and recurring billing, + but delegates external side effects through those services. +- All Prisma access uses the singleton exported by `src/lib/db.ts`. + +See [ARCHITECTURE.md](ARCHITECTURE.md) for a shorter architectural tour and +[AGENTS.md](AGENTS.md) for repository-specific coding guidance. + +## Core business flows + +### Checkout and first activation + +```text +Product configurator + → cookie cart + → checkout captcha/fraud/VAT checks + → priceCart() + computeTotals() + → Order + OrderItems + pending Services + Invoice (one transaction) + → gateway / credits / manual / free payment + → markInvoicePaid() + → Service ACTIVE + → provision server and/or resale product + → notification, email, audit, affiliate commission +``` -| Admin dashboard | Product editor | Extensions | -|---|---|---| -| ![Admin](docs/screenshots/admin-dashboard.png) | ![Product editor](docs/screenshots/admin-product-edit.png) | ![Extensions](docs/screenshots/admin-extensions.png) | +Orders sent to fraud review can be paid, but their services stay pending until +an administrator approves the order. Provisioning failures are recorded in the +audit log rather than undoing a received payment. -
-More screenshots +### Renewal automation -| | | -|---|---| -| ![Category](docs/screenshots/storefront-category.png) | ![Login](docs/screenshots/auth-login.png) | -| ![Services](docs/screenshots/client-services.png) | ![Account](docs/screenshots/client-account.png) | -| ![Products](docs/screenshots/admin-products.png) | ![Invoices](docs/screenshots/admin-invoices.png) | -| ![Admin ticket](docs/screenshots/admin-ticket.png) | ![Settings](docs/screenshots/admin-settings.png) | +`POST /api/cron`, authenticated with `CRON_SECRET`, is intended to run hourly: -
+```text +generate renewal invoices + → add unbilled usage records + → auto-charge due invoices when a default stored method supports it + → execute end-of-term cancellations + → suspend overdue active services + → terminate services suspended past the configured window + → prune old failed-login attempts +``` + +The timing values are runtime settings: + +- `invoice_days_before` defaults to `7`. +- `suspend_days_after` defaults to `2`. +- `cancel_days_after` defaults to `14` days after suspension. + +Successful renewal payment extends the service from its current expiry date +when possible and unsuspends it through the configured driver if necessary. + +### Request authentication + +- Browser sessions use the `oh_session` HTTP-only, `SameSite=Lax` cookie. +- `requireUser()` protects customer pages and actions. +- `requireAdmin(permission)` requires a staff role, the requested permission, + and staff 2FA when that setting is enabled. +- REST requests use `Authorization: Bearer oh_…` API keys and per-route scopes. +- The billing cron uses its own `CRON_SECRET` bearer token. +- Gateway webhook authenticity is handled inside each gateway driver. + +## Repository map + +```text +. +├── src/ +│ ├── app/ +│ │ ├── (store)/ public store, cart, blog, knowledgebase +│ │ ├── (auth)/ login, registration, reset, two-factor flow +│ │ ├── dashboard/ authenticated customer area +│ │ ├── admin/ staff administration area +│ │ ├── api/v1/ scoped REST API +│ │ ├── api/webhooks/ gateway callbacks +│ │ ├── api/cron/ recurring billing tick +│ │ └── oauth/ authorization-code token and user-info routes +│ ├── components/ shared forms, status, locale/currency UI +│ ├── generated/prisma/ generated and git-ignored Prisma client +│ └── lib/ +│ ├── actions/ Server Actions organized by domain +│ ├── services/ domain workflows and integration boundaries +│ ├── extensions/ integration types, registry, and drivers +│ ├── auth.ts sessions, password hashing, RBAC, tokens +│ ├── billing.ts payment effects and recurring billing policy +│ ├── db.ts shared Prisma client +│ ├── i18n.ts locale dictionaries and translation helpers +│ ├── mail.ts SMTP and editable templates +│ └── settings.ts database settings with code defaults +├── prisma/ +│ ├── schema.prisma domain schema +│ ├── migrations/ deployable SQL migration history +│ ├── seed.ts idempotent development/install seed +│ └── reset-admin.ts account recovery utility +├── cli/ `oh` REST API client +├── mcp/ stdio MCP server +├── scripts/ importers and screenshot generation +├── deploy/k8s/ Kubernetes Deployment, HPA, Ingress, CronJob +├── docs/ operator, feature, integration, and API guides +├── Dockerfile multi-stage standalone production image +├── docker-compose.yml PostgreSQL, app, and hourly cron services +├── docker-entrypoint.sh migrate-on-start behavior +├── install.sh interactive installer and install manager +└── next.config.ts standalone build configuration +``` + +## Local development -## 🚀 Quick start +### Requirements -One command on a fresh Linux server — it installs Docker if needed, asks for -your port, domain and admin login, generates secrets, starts the stack, seeds -the account, and sets up nginx with a free HTTPS certificate for your domain: +- Node.js 24 or newer and npm. +- PostgreSQL 14 or newer, or a Supabase PostgreSQL project. +- Git. +- Docker is optional, but is the quickest way to start a local database. + +### 1. Install dependencies and configure the environment ```bash -curl -fsSL https://raw.githubusercontent.com/solomon2773/openhosting/main/install.sh | bash +git clone https://github.com/solomon2773/openhosting.git +cd openhosting +npm ci +cp .env.example .env ``` -Give it a domain when it asks (or pass `--domain billing.example.com`) and it -comes up on `https://` with auto-renewing Let's Encrypt certificates — or leave -it empty to serve plain HTTP until DNS is ready. If port 3000 is taken it offers -the free ports next to it instead of failing halfway through. +The default `.env.example` points to PostgreSQL on `localhost:5432` with the +database, user, and password all set to `openhosting`. + +### 2. Start PostgreSQL + +Skip this step if `.env` points to an existing database. + +```bash +docker run -d --name oh-db -p 5432:5432 \ + -e POSTGRES_USER=openhosting \ + -e POSTGRES_PASSWORD=openhosting \ + -e POSTGRES_DB=openhosting \ + postgres:18-alpine +``` -Run the same command again to manage that install: upgrade, rebuild, **reset -the admin password**, change the port or domain, reinstall or uninstall. Every -action is also a flag, so it scripts cleanly: +### 3. Generate the client, initialize data, and run the app ```bash -curl -fsSL …/install.sh | bash -s -- --upgrade # unattended update -curl -fsSL …/install.sh | bash -s -- --reset-password # locked out +npm run db:generate +npm run db:push +npm run db:seed +npm run dev ``` -See the [installer reference](docs/getting-started/docker.md#one-line-install-recommended). +Open . + +`db:push` is intended for local development. Use migrations for schema changes +that will be committed, and `npm run db:migrate` in production. + +### Seeded accounts and data + +| Account | Email | Password | +|---|---|---| +| Administrator | `admin@example.com` | `admin12345` | +| Demo customer | `demo@example.com` | `demo12345` | + +The seed is idempotent: it creates missing roles, users, demo catalog data, +extensions, settings, templates, example billing activity, and knowledgebase +articles without overwriting operator-modified records. Set +`SEED_ADMIN_EMAIL` and `SEED_ADMIN_PASSWORD` before the first seed to choose +different administrator credentials. + +Never keep the default administrator password on a reachable deployment. + +## Configuration + +OpenHosting intentionally separates deploy-time environment variables from +runtime business settings. + +### Environment variables + +| Variable | Required | Purpose | +|---|---:|---| +| `DATABASE_URL` | Yes | Runtime PostgreSQL connection; use a pooled URL for Supabase | +| `DIRECT_URL` | Yes | Direct connection used by Prisma migrations; may equal `DATABASE_URL` on plain PostgreSQL | +| `CRON_SECRET` | Yes in production | Protects `POST /api/cron`; generate with `openssl rand -hex 32` | +| `SHADOW_DATABASE_URL` | No | Separate shadow database used while creating migrations locally | +| `SKIP_MIGRATIONS` | No | Set to `true` when the platform applies migrations outside each app container | +| `SEED_ADMIN_EMAIL` | No | Initial seeded administrator email | +| `SEED_ADMIN_PASSWORD` | No | Initial seeded administrator password | +| `APP_URL` | No | Lets the seed replace the placeholder public URL on a fresh install | +| `PORT` | No | Next.js listen port; defaults to `3000` | +| `WHMCS_DB_URL` | No | Source MySQL DSN for the WHMCS importer | +| `PAYMENTER_DB_URL` | No | Source MySQL DSN for the Paymenter importer | + +See the complete [environment reference](docs/getting-started/environment.md). + +### Runtime settings -
-Manual Docker setup +Company identity, public URL, base currency, themes, billing timing, taxes, +registration, security controls, fraud providers, affiliate behavior, AI +features, and SMTP are stored in the `Setting` table and edited under +**Admin → Settings**. Extension credentials and product-specific driver values +are edited under **Admin → Extensions** and **Admin → Products**. + +Set the public URL to the final HTTPS origin before enabling email or live +payment gateways. Request-time links can infer an origin on a fresh install, +but emailed links deliberately use only the configured public URL to prevent +Host-header steering. + +## Commands + +| Command | Purpose | +|---|---| +| `npm run dev` | Run the development server with hot reload | +| `npm run build` | Build the standalone production application | +| `npm run start` | Start a previously built application | +| `npm run typecheck` | Run strict TypeScript checking without emitting files | +| `npm run db:generate` | Generate Prisma into `src/generated/prisma` | +| `npm run db:push` | Synchronize the schema directly for local development | +| `npm run db:migrate` | Apply committed migrations with `prisma migrate deploy` | +| `npm run db:seed` | Run the idempotent seed | +| `npm run db:reset-admin -- --list` | List staff accounts for recovery | +| `npm run db:reset-admin -- --email … --password-stdin` | Reset a staff password without putting it in shell history | +| `npm run import:whmcs` | Import supported records from a WHMCS MySQL database | +| `npm run import:paymenter` | Import supported records from a Paymenter MySQL database | +| `npm run cli -- ` | Run the `oh` CLI from the repository | +| `npm run mcp` | Start the OpenHosting MCP server over stdio | +| `npx tsx scripts/screenshots.ts` | Refresh README/docs screenshots from a running seeded app | + +For a schema change, create and inspect a migration rather than committing only +the schema edit: ```bash -git clone https://github.com/solomon2773/openhosting.git -cd openhosting -export DB_PASSWORD="$(openssl rand -hex 16)" CRON_SECRET="$(openssl rand -hex 32)" -docker compose up -d --build -docker compose exec -e SEED_ADMIN_PASSWORD="a-strong-password" app node prisma/seed.mjs +npx prisma migrate dev --name describe_the_change +npm run db:generate ``` -Open — admin login `admin@example.com` with the -password you chose. See the [Docker guide](docs/getting-started/docker.md). +## APIs and automation -
+### REST API -### Local development +The versioned API lives at `/api/v1`. API keys are created in **Admin → API +keys**, their raw value is displayed once, and only a SHA-256 hash is stored. +Routes are protected by scopes such as `users:read`, `services:write`, and +`usage:write`. ```bash -npm install -cp .env.example .env # point DATABASE_URL at Postgres or Supabase -npm run db:push && npm run db:seed -npm run dev +curl -H "Authorization: Bearer $OPENHOSTING_API_KEY" \ + https://billing.example.com/api/v1/services ``` -## 📚 Documentation +Resources currently include users, products, categories, orders, invoices, +services, metered usage, coupons, quotes, tickets, and knowledgebase search. +See the [REST API reference](docs/api/rest-api.md) for methods, payloads, +pagination, and scopes. + +### OAuth2 + +OpenHosting can act as an OAuth2 authorization-code provider for other +applications. Administrators create clients in the admin panel; authorization +codes and access tokens are stored as hashes, redirect URIs are exact matched, +and `/oauth/userinfo` returns the authenticated user's profile. See the +[OAuth/SSO guide](docs/api/oauth.md). -Full documentation lives in **[docs/](docs/README.md)** — start with the -[Introduction](docs/getting-started/introduction.md). Highlights: +### CLI -| | | | +The zero-dependency `oh` CLI uses the REST API: + +```bash +export OPENHOSTING_URL="https://billing.example.com" +export OPENHOSTING_API_KEY="oh_…" +npm run cli -- services list --status ACTIVE +``` + +It can also persist configuration under `~/.openhosting/config.json`. See the +[CLI guide](docs/cli.md) before saving secrets on a shared workstation. + +### MCP server + +`mcp/server.mjs` exposes 21 typed tools over stdio for customers, catalog, +orders, invoices, services, usage, coupons, quotes, support, knowledgebase, and +the billing cron. It uses the same API-key scopes as the REST API. See the +[MCP setup guide](docs/mcp.md). + +### Agent commerce + +Customers can create expiring, spend-capped purchasing grants under +**Dashboard → Account → Agent access**. Agents discover the public catalog at +`/api/agent/catalog`, place idempotent native orders, use ACP 2026-04-17 with a +Stripe Shared Payment Token for fiat checkout, or settle a USDC invoice through +the x402 v2 gateway. Start with the [agent commerce guide](docs/guides/agent-commerce.md); +all protocol and payment integrations are disabled until explicitly configured. + +## Extension system + +Extension contracts live in `src/lib/extensions/types.ts`: + +| Driver | Required lifecycle | Optional capabilities | |---|---|---| -| [Installation](docs/getting-started/installation.md) | [Docker](docs/getting-started/docker.md) | [Kubernetes](docs/getting-started/kubernetes.md) | -| [Configuration](docs/getting-started/configuration.md) | [Products](docs/guides/products.md) | [Billing automation](docs/billing/automation.md) | -| [Payment gateways](docs/extensions/payment-gateways.md) | [Server modules](docs/extensions/server-modules.md) | [Resale modules](docs/extensions/resale-modules.md) | -| [Fraud protection](docs/guides/fraud.md) | [Affiliates](docs/guides/affiliates.md) | [Tickets](docs/guides/tickets.md) | -| [REST API](docs/api/rest-api.md) | [OAuth / SSO](docs/api/oauth.md) | [Writing extensions](docs/extensions/writing-extensions.md) | -| [Themes](docs/guides/themes.md) | [Migrations](docs/migrations.md) | [FAQ](docs/faq.md) | -| [CLI](docs/cli.md) | [MCP server](docs/mcp.md) | [Roadmap](docs/roadmap.md) | - -## 🏗 Architecture - -OpenHosting is a single Next.js App Router application following SOLID -principles: payment gateways and provisioning backends are substitutable -drivers behind segregated interfaces, and the billing engine depends only on -abstractions — adding an integration never touches core code. Read the full -tour in [ARCHITECTURE.md](ARCHITECTURE.md). +| `GatewayDriver` | Start invoice payment | Webhook handling, stored-method setup, off-session charging | +| `ServerDriver` | Create, suspend, unsuspend, terminate | Product-specific configuration fields | +| `ResaleDriver` | Provision and cancel | Renew; checkout fields such as domain, CSR, or seat count | +| `AiDriver` | Complete a text request | Schema-constrained JSON completion | + +Every driver declares its global and product configuration fields. The admin +UI renders those definitions, and database `Extension` rows store enabled state +and configuration. `src/lib/extensions/registry.ts` is the in-code registry; +the admin layout synchronizes missing database rows so a newly shipped driver +appears without a schema migration. + +To add an integration: + +1. Implement the appropriate interface in `gateways/`, `servers/`, `resale/`, + or `ai/`. +2. Register it in `src/lib/extensions/registry.ts`. +3. Keep the seed's extension inventory and the appropriate documentation page + in sync. +4. Keep protocol handling inside the driver and business policy inside the + service layer. +5. Run Prisma generation, type checking, and a production build. + +Read [Writing an extension](docs/extensions/writing-extensions.md) and the +localized [extension guidance](src/lib/extensions/AGENTS.md) before starting. + +## Database and data model + +`prisma/schema.prisma` is grouped by domain. Major relationships are: + +```text +User +├── sessions, tokens, role, contacts, API keys, agent grants, payment methods +├── orders ── order items ── products ── categories/prices/options +├── services ── usage records +├── invoices ── invoice items ── payments +├── agent checkouts ── idempotent operations / x402 settlements +├── tickets ── messages ── attachments +├── quotes ── quote items +└── affiliate, notifications, audit records + +Product +├── server extension + product server config +├── resale extension + product resale config +└── upgrade paths, coupon eligibility, metered billing settings +``` + +Money uses PostgreSQL decimal columns. The base currency lives in settings; +credit and affiliate balances use that base, while orders, invoices, payments, +and services retain their transaction currency. Flexible integration and +checkout configuration is stored as JSON. + +The generated Prisma client is intentionally outside `node_modules`, under +`src/generated/prisma`, and is git-ignored. Import generated types from +`@/generated/prisma/client`, never from `@prisma/client`. + +## Security model + +- Passwords use bcrypt; reset/verification tokens, API keys, OAuth secrets, + authorization codes, and OAuth access tokens are stored as hashes. +- Browser sessions are opaque database records with a 14-day expiry and secure + cookies in production. +- Admin access uses database roles and permission arrays; `*` grants every + permission. +- Sensitive mutations should create an audit entry with actor, target, client + IP, and useful non-secret metadata. +- Login throttling can enforce both per-account and per-IP budgets. +- Gateway drivers are responsible for verifying their provider's webhook + signature before returning a payment result. +- Ticket uploads allow at most three files per message, 5 MB each, from an + explicit MIME allowlist; attachment bytes are stored in PostgreSQL. +- Integration configuration is application data in the database. Protect the + database, backups, admin accounts, and deployment secrets accordingly. +For vulnerability reporting, see [SECURITY.md](SECURITY.md). + +## Testing and continuous integration + +There is currently no automated unit or end-to-end test suite. The CI workflow +is the minimum merge gate and runs: + +1. `npm ci`. +2. `npx prisma generate`. +3. `npm run typecheck`. +4. `npm run build`. +5. A separate multi-stage Docker image build. + +Run at least the same typecheck and build locally before opening a pull request: + +```bash +npm run db:generate +npm run typecheck +npm run build ``` -Next.js (App Router, Server Actions) -├── Storefront ├── Client area ├── Admin panel └── REST API /api/v1 -src/lib -├── billing.ts invoice lifecycle & recurring billing -├── services/ orders, payments, provisioning (SOLID service layer) -└── extensions/ GatewayDriver + ServerDriver implementations -PostgreSQL / Supabase (Prisma) + +For changes to billing, payments, authentication, migrations, or provisioning, +also exercise the affected workflow against an isolated database. Playwright is +present for deterministic screenshot capture, not yet as a behavioral test +suite. + +## Deployment + +### One-line installer + +On a fresh Linux host, the installer can install Docker, generate secrets, +choose a port, start and seed the stack, and optionally configure nginx with a +Let's Encrypt certificate: + +```bash +curl -fsSL https://raw.githubusercontent.com/solomon2773/openhosting/main/install.sh | bash ``` -## 🤝 Contributing & community +Re-running it offers upgrade, rebuild, admin recovery, reconfiguration, +reinstall, and uninstall actions. Destructive actions ask for confirmation. +See the [installer and Docker guide](docs/getting-started/docker.md). + +### Docker Compose + +The repository Compose stack contains PostgreSQL, the application, and an +hourly curl-based cron service: + +```bash +export DB_PASSWORD="$(openssl rand -hex 16)" +export CRON_SECRET="$(openssl rand -hex 32)" +docker compose up -d --build +docker compose exec -e SEED_ADMIN_PASSWORD="a-strong-password" \ + app node prisma/seed.mjs +``` + +The container entrypoint applies `prisma migrate deploy` before starting the +server. Set `SKIP_MIGRATIONS=true` only when the deployment platform guarantees +that migrations run separately. + +### Kubernetes + +`deploy/k8s` includes a namespace, secret example, two-replica Deployment, +Service, nginx Ingress, HPA, and hourly billing CronJob. A Deployment init +container applies migrations while app containers skip entrypoint migrations. +Replace the example image, host, issuer, resources, and secrets for your cluster. +See the [Kubernetes guide](docs/getting-started/kubernetes.md). + +### Other platforms + +Any platform that can run the standalone Node.js output and reach PostgreSQL +can host the application. It must also invoke `POST /api/cron` on a schedule and +forward the original host/protocol headers correctly behind a proxy. Supabase +users should use the pooled URL for runtime access and the direct URL for +migrations. + +## Development conventions + +- Keep Server Components focused on reads and rendering. Put UI mutations in a + domain Server Action and reusable business behavior in a service. +- Authenticate and authorize again inside every mutation; hiding a button is + not an access-control boundary. +- Preserve the driver/service dependency boundary. Billing and pages should not + import concrete drivers. +- Use `db` from `src/lib/db.ts` and imports from the generated client path. +- Add a committed migration for schema changes. Never edit or reorder an + already deployed migration. +- Keep `prisma/seed.ts` idempotent because installers may run it again during an + update. +- Prisma 7 standalone scripts must load `.env` explicitly when they need local + environment files; follow the pattern in the seed and importers. +- Add translated keys to `src/lib/i18n.ts` for pages that already use `getT()`; + the English dictionary defines the valid key type. +- Add an audit record for sensitive admin, account, billing, authentication, or + lifecycle mutations without logging secrets. +- Update deployment examples and operator docs when environment variables, + startup behavior, or scheduled work changes. +- Refresh generated screenshots after changing UI shown in this README. + +Repository-specific automation guidance is in [AGENTS.md](AGENTS.md), with +additional scoped files only where the workflow differs materially. + +## Screenshots + +| Storefront | Product configurator | Cart | +|---|---|---| +| ![Storefront](docs/screenshots/storefront-home.png) | ![Product](docs/screenshots/storefront-product.png) | ![Cart](docs/screenshots/storefront-cart.png) | + +| Client dashboard | Invoice payment | Support ticket | +|---|---|---| +| ![Dashboard](docs/screenshots/client-dashboard.png) | ![Invoice](docs/screenshots/client-invoice-pay.png) | ![Ticket](docs/screenshots/client-ticket.png) | + +| Admin dashboard | Product editor | Extensions | +|---|---|---| +| ![Admin](docs/screenshots/admin-dashboard.png) | ![Product editor](docs/screenshots/admin-product-edit.png) | ![Extensions](docs/screenshots/admin-extensions.png) | + +## Documentation + +The complete operator and feature documentation starts at +[docs/README.md](docs/README.md). + +| Getting started | Product and operations | Extending and automating | +|---|---|---| +| [Installation](docs/getting-started/installation.md) | [Products](docs/guides/products.md) | [Extensions overview](docs/extensions/overview.md) | +| [Docker](docs/getting-started/docker.md) | [Orders and invoices](docs/guides/orders-invoices.md) | [Writing extensions](docs/extensions/writing-extensions.md) | +| [Kubernetes](docs/getting-started/kubernetes.md) | [Billing automation](docs/billing/automation.md) | [REST API](docs/api/rest-api.md) | +| [Supabase](docs/getting-started/supabase.md) | [Accounts and security](docs/guides/accounts-security.md) | [OAuth / SSO](docs/api/oauth.md) | +| [Configuration](docs/getting-started/configuration.md) | [Fraud protection](docs/guides/fraud.md) | [CLI](docs/cli.md) | +| [Environment](docs/getting-started/environment.md) | [Support tickets](docs/guides/tickets.md) | [MCP server](docs/mcp.md) | + +For architectural rationale, see [ARCHITECTURE.md](ARCHITECTURE.md). For setup +problems and operational questions, see the [FAQ](docs/faq.md). -PRs are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for the dev setup and -how to add a payment gateway or server integration (usually < 100 lines). -Check the [good first issues](https://github.com/solomon2773/openhosting/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) -for scoped starting points, or ask questions in -[Discussions](https://github.com/solomon2773/openhosting/discussions). +## Contributing -⭐ **If OpenHosting looks useful to you, a star helps other hosting -providers find it.** +Issues and pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md), +the architecture notes, and the applicable `AGENTS.md` before changing code. +The [good first issues](https://github.com/solomon2773/openhosting/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) +are intended to be narrowly scoped. -## 📄 License +## License -[MIT](LICENSE) — free for commercial use. +[MIT](LICENSE) — free for personal and commercial use. diff --git a/docs/getting-started/environment.md b/docs/getting-started/environment.md index c5d69bd..96a3103 100644 --- a/docs/getting-started/environment.md +++ b/docs/getting-started/environment.md @@ -13,6 +13,7 @@ currency and SMTP is configured in the admin panel instead — see | `SKIP_MIGRATIONS` | | `true` skips `prisma migrate deploy` in the container entrypoint (when migrations run elsewhere). | | `SEED_ADMIN_PASSWORD` | | Admin password used by `npm run db:seed`, applied only when the account is created. Defaults to `admin12345`. | | `SEED_ADMIN_EMAIL` | | Address of the admin account the seed creates. Defaults to `admin@example.com`; the installer writes the address you chose here so re-seeding never adds a second admin. | +| `APP_URL` | | Public origin used by the seed on a fresh install. Runtime links use the **Admin → Settings → Public URL** value. | | `ATTACHMENT_STORAGE` | | `database` (default) or `s3` for new ticket attachments. Existing rows retain their original backend. | | `ATTACHMENT_S3_BUCKET` | S3 only | Private bucket used for attachments. | | `ATTACHMENT_S3_REGION` | S3 only | Bucket signing region. Defaults to `us-east-1`; R2 commonly uses `auto`. | @@ -43,6 +44,7 @@ CRON_SECRET="change-me-openssl-rand-hex-32" platform's secret store in production — a Kubernetes `Secret`, Docker secrets, or your PaaS's environment configuration — not committed to git. - `.env` is git-ignored. Copy `.env.example` to start. -- Gateway and provisioning credentials are **not** environment variables — - they're stored (encrypted at rest by your database) via the admin - [Extensions](../extensions/overview.md) UI. +- Gateway, provisioning, resale, and AI credentials are **not** environment + variables — they're stored as extension configuration in PostgreSQL via the + [Extensions](../extensions/overview.md) UI. Protect the database and backups, + and enable storage encryption in your database platform. diff --git a/docs/roadmap.md b/docs/roadmap.md index d8934f5..db2a1c4 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -7,10 +7,11 @@ x402) with scoped-permission safety, bring-your-own model keys, and human-in-the-loop defaults. **Foundations already shipped:** [AI support](guides/ai-support.md) — staff-reviewed -reply drafts grounded in your knowledgebase, and ticket classification, both on -your own API key — the [MCP server](mcp.md) (21 tools), the +reply drafts, ticket classification, confidence-gated tier-1 resolution, and a +read-only customer assistant, all on your own API key — the [MCP server](mcp.md) (21 tools), the scoped [REST API](api/rest-api.md), an [OAuth2 provider](api/oauth.md), [usage-metered billing](guides/metered-billing.md), the +[delegated agent-commerce APIs](guides/agent-commerce.md), the [knowledgebase](guides/knowledgebase.md), the [fraud pipeline](guides/fraud.md), and the idempotent [billing cron](billing/automation.md). @@ -25,18 +26,20 @@ contributions and design feedback shape the ordering. **Shipped:** auto-drafted replies for staff review and auto-triage (department/priority) on ticket creation, on a bring-your-own Anthropic key with per-feature toggles — see [AI support](guides/ai-support.md). - **Still to come:** optional auto-resolve for tier-1 questions behind a - confidence threshold, and additional providers. Industry benchmarks put AI-first + **Shipped:** optional auto-resolve for tier-1 questions behind source, + priority, race, and confidence gates. **Still to come:** additional providers. + Industry benchmarks put AI-first support at 60–80% deflection at a fraction of human cost — for ticket-heavy, thin-margin hosting businesses this is the single biggest cost lever. -2. **Customer-facing assistant** — a scoped chat in the client area grounded - in the knowledgebase *plus the customer's own services and invoices* - (read-only), with an "escalate to ticket" handoff. -3. **MCP v2: remote & trusted** — streamable-HTTP transport secured by the +2. **Customer-facing assistant — shipped** — a scoped chat in the client area + grounded in the knowledgebase *plus the customer's own services and invoices* + (read-only), with an "escalate to ticket" handoff and per-account rate limit. +3. **MCP v2: remote & trusted — shipped** — streamable-HTTP transport secured by the built-in OAuth provider so hosted AI clients connect without a local - install; per-key *tool-level* scoping in the admin UI; listing in the MCP - Registry. + install; per-key *tool-level* scoping in the admin UI; and discovery + metadata ready for registry submission. **Still to come:** publication in + the external MCP Registry. 4. **`llms.txt` + AI-readable docs** — so AI assistants answer OpenHosting questions accurately. diff --git a/prisma/AGENTS.md b/prisma/AGENTS.md new file mode 100644 index 0000000..af75074 --- /dev/null +++ b/prisma/AGENTS.md @@ -0,0 +1,52 @@ +# Prisma and database guidance + +This file applies to `prisma/`. + +## Source and generated client + +- `schema.prisma` is the source of truth for the relational model. +- Prisma 7 generates into `src/generated/prisma`. Never commit or hand-edit that + directory, and import from `@/generated/prisma/client` in application code. +- Runtime queries use the `@prisma/adapter-pg` client singleton in + `src/lib/db.ts`. Standalone scripts create their own adapter-backed client. + +## Connections + +- The application uses `DATABASE_URL`, which may be pooled. +- Prisma CLI/migrations prefer `DIRECT_URL`; plain PostgreSQL can use the same + value for both. +- Migration creation may require `SHADOW_DATABASE_URL`. +- Prisma 7 standalone TypeScript scripts do not automatically load `.env`. + Follow the guarded `process.loadEnvFile(".env")` pattern in `seed.ts`. + +## Schema and migrations + +- Use `npm run db:push` only for disposable/local development databases. +- Commit a migration for every deployable schema change and inspect the SQL. +- Existing migration directories are ordered and already deployable. Never + edit, rename, squash, or reorder a migration that may have been applied. +- Prefer additive, backward-compatible changes when app and migration rollout + order can overlap. Plan data backfills and destructive changes explicitly. +- Regenerate the client after schema changes and run typecheck/build. + +## Seed and recovery scripts + +- `seed.ts` must remain idempotent. Install and upgrade flows can re-run it, so + use upserts or existence checks and never overwrite operator-configured data. +- Keep administrator environment overrides working and never print or persist a + generated secret unexpectedly. +- Keep the extension inventory synchronized with `src/lib/extensions/registry.ts`. +- `reset-admin.ts` is an account-recovery path used both from source and as a + bundled container script. Preserve non-interactive `--password-stdin` support + and safe account selection. + +## Verification + +For schema changes, test both a fresh database and migration of a representative +existing database when risk warrants it. At minimum run: + +```bash +npm run db:generate +npm run typecheck +npm run build +``` diff --git a/src/app/AGENTS.md b/src/app/AGENTS.md new file mode 100644 index 0000000..8fc273c --- /dev/null +++ b/src/app/AGENTS.md @@ -0,0 +1,37 @@ +# App Router guidance + +This file applies to `src/app`. + +## Responsibilities + +- Pages and layouts are Server Components by default. Keep them focused on + authorization, reads, and rendering. +- Put browser-facing mutations in a domain Server Action under + `src/lib/actions`; put reusable workflows in `src/lib/services`. +- Route handlers are reserved for REST resources, gateway webhooks, the billing + cron, OAuth, verification, referrals, and authorized file delivery. +- The root layout uses `dynamic = "force-dynamic"`. Do not introduce build-time + database access or static rendering assumptions without redesigning that + constraint explicitly. + +## Access control + +- Dashboard pages require `requireUser()` and must scope customer-owned records + to that user. +- Admin pages/actions require `requireAdmin()` with the narrowest existing + permission that fits the feature. +- API v1 handlers use `withApiKey()` and an explicit read/write scope. Return + JSON errors consistently and validate request bodies with Zod. +- Attachment routes must check that the requesting user owns the ticket or has + appropriate staff access before returning database bytes. + +## Mutations and rendering + +- Revalidate affected paths after a successful mutation, or redirect to a page + that reads the new state. +- Do not call concrete extension drivers from a page, action, or route. Use the + relevant service boundary. +- Use settings helpers for configurable behavior and `getT()` for areas already + localized. Keep user-visible errors specific without exposing credentials or + provider response bodies. +- Add audit records to security-sensitive and lifecycle-changing mutations. diff --git a/src/lib/extensions/AGENTS.md b/src/lib/extensions/AGENTS.md new file mode 100644 index 0000000..cba5dd6 --- /dev/null +++ b/src/lib/extensions/AGENTS.md @@ -0,0 +1,45 @@ +# Extension driver guidance + +This file applies to payment, server, resale, and AI drivers in +`src/lib/extensions`. + +## Contracts and registration + +- Implement the appropriate interface from `types.ts`; do not add provider + policy to a page or to the billing engine. +- Add the driver to the matching array in `registry.ts`. Keep the seed inventory + and extension documentation in sync when adding or renaming a driver. +- Driver slugs are durable database and URL identifiers. Do not rename one + without a data migration and compatibility plan. +- Declare operator credentials in `configFields`, product mapping in + `productConfigFields`, and customer input only in resale `checkoutFields`. + +## Boundaries + +- Only `services/payments.ts` resolves gateway drivers. +- Only `services/provisioning.ts` resolves server drivers. +- Only `services/resale.ts` resolves resale drivers. +- AI prompt policy and feature flags belong in `services/ai.ts`; AI drivers only + translate typed requests into provider calls. +- Keep files server-only. Use native `fetch` unless an existing dependency is + already required and materially improves correctness. + +## Lifecycle and safety + +- Gateway webhooks must verify the provider signature and payment state before + returning an invoice/transaction pair. Make repeat delivery safe; invoice + payment handling is idempotent after an invoice is paid. +- Server create methods return the external resource ID. Suspend, unsuspend, + and terminate must use the stored `externalId` where the provider requires it. +- Resale provision returns an external reference; renew is optional; cancel is + required. +- Do not log API keys, secrets, raw webhook signatures, card data, access + tokens, or full provider payloads. Return operator-actionable error messages. +- Preserve the service layer's failure behavior: external lifecycle failures are + audited so a provider outage does not silently rewrite billing state. + +## Verification + +At minimum run `npm run db:generate`, `npm run typecheck`, and `npm run build`. +For a live driver, also exercise the provider's sandbox or test mode, including +signature failure, success, and repeat webhook delivery where applicable. From 62aa785f781395150d0701be767b0e27291b9c94 Mon Sep 17 00:00:00 2001 From: solomon2773 Date: Thu, 27 Aug 2026 15:57:23 -0500 Subject: [PATCH 2/2] docs: expand agent commerce positioning --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 286f652..27231ca 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ # 🚀 OpenHosting **Open-source billing and client management for hosting providers.**
-Storefront, recurring billing, provisioning, product resale, and support in one -self-hosted application. +Storefront, recurring billing, provisioning, support, and agent-native commerce +in one self-hosted application. [![CI](https://img.shields.io/badge/CI-GitHub_Actions-2088FF?logo=githubactions&logoColor=white)](.github/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) @@ -19,6 +19,7 @@ self-hosted application. ## Contents - [What OpenHosting includes](#what-openhosting-includes) +- [Agent commerce, built in](#agent-commerce-built-in) - [Technology stack](#technology-stack) - [How the application is structured](#how-the-application-is-structured) - [Core business flows](#core-business-flows) @@ -43,6 +44,11 @@ OpenHosting keeps those concerns in one TypeScript application backed by PostgreSQL. It can sell game servers, VPS and web hosting, as well as domains, SSL certificates, software licenses, and productivity-suite seats. +The same catalog can serve human customers and autonomous buyers. Agents can +discover products, receive bounded purchasing authority from an existing +customer, create an order through a native API or ACP, and pay an exact USDC +invoice through x402 without creating a second commerce or provisioning stack. + The application has four primary surfaces: 1. A public storefront and knowledgebase. @@ -123,6 +129,58 @@ transfer method created by the development seed. - Six runtime-selectable themes and five included locales: English, Dutch, French, German, and Spanish. +## Agent commerce, built in + +> **Build the hosting product once. Sell it through the storefront, an AI +> agent, or a machine-to-machine workflow using the same price, invoice, +> fraud, payment, and provisioning engine.** + +OpenHosting treats autonomous purchasing as a first-class channel rather than +an unscoped API key bolted onto checkout: + +- **AI-readable discovery.** `/.well-known/agent-commerce` advertises a public + catalog containing stable SKUs, billing cycles, configuration choices, + availability, metered pricing, currencies, precision, settlement networks, + and supported checkout protocols. +- **Delegated purchasing authority.** A customer can issue an expiring agent + grant restricted to exact products and currencies, with both a maximum order + value and a cumulative spend ceiling. The bearer credential is displayed + once, stored only as a hash, auditable, and immediately revocable. +- **Idempotent native checkout.** Agents can place orders through JSON without + risking duplicate infrastructure. OpenHosting reloads stock, prices, options, + coupons, tax, fraud policy, currency rates, and grant limits before creating + the normal order, service, and invoice records. +- **Agentic Commerce Protocol.** The versioned ACP checkout-session adapter + supports cart creation, retrieval, updates, completion, and cancellation, + with delegated fiat payment through Stripe Shared Payment Tokens. +- **USDC machine payments.** The x402 v2 gateway issues exact six-decimal USDC + requirements, binds signed payment terms to one invoice and URL, verifies and + settles through a configured facilitator, and prevents transaction replay. +- **Metered autonomous infrastructure.** Usage records retain sub-cent + precision and settle in the service's locked currency, supporting + pay-per-request, pay-per-hour, bandwidth, storage, and compute resale models. +- **One operational ledger.** Agent orders use the existing invoices, payment + records, fraud review, audit log, lifecycle automation, and provisioning + drivers. Operators do not reconcile a separate agent-commerce backend. + +```text +agent discovers catalog + → customer grants bounded authority + → native checkout or ACP session + → fiat via Stripe SPT or USDC via x402 + → normal OpenHosting invoice, service lifecycle, and provisioning +``` + +This foundation is designed for autonomous deployment assistants, AI +infrastructure marketplaces, usage-based compute sellers, hosting resellers, +and internal agents that purchase within a customer-approved budget. Anonymous +autonomous account creation is intentionally later on the roadmap; current +grants remain anchored to an existing customer and its identity, recovery, +fraud, and compliance controls. + +See the [agent commerce guide](docs/guides/agent-commerce.md) for API examples, +security boundaries, ACP requirements, and x402 deployment guidance. + ## Technology stack | Layer | Technology | Notes |