import Link from "next/link"; import type { ReactNode } from "react"; import type { Metadata } from "next"; import { SiteShell } from "@/components/site-shell"; import { mono, pre, table, td, th } from "../openontology/ui"; export const metadata: Metadata = { title: "OpenSaaS ยท LogicSRC", description: "OpenSaaS is one file a subscription service serves at /.well-known/opensaas.json about how to deal with it: its plans, and how to subscribe, cancel, pause, change plan, stop the mail, export and delete, each as the page a person opens and the endpoint an agent calls with an OpenAccess scope. The way out, written down beside the way in.", alternates: { canonical: "/opensaas" } }; const DESCRIPTOR = `{ "service": { "name": "NicheDB", "web": "https://nichedb.dev", "openaccess": "https://nichedb.dev/.well-known/openaccess.json", "terms": "https://nichedb.dev/terms", "currency": "USD" }, "updated": "2026-09-13T06:00:00Z", "plans": [ { "id": "premium", "name": "Premium", "price": 1, "period": "day", "renews": true, "url": "https://nichedb.dev/premium", "includes": ["no ads", "no tracker"] }, { "id": "pro", "name": "Pro", "price": 30, "period": "month", "trial": "14 days", "renews": true, "url": "https://nichedb.dev/pro" } ], "actions": { "subscribe": { "page": "https://nichedb.dev/premium", "api": { "method": "POST", "url": "https://nichedb.dev/api/v1/billing/subscribe" }, "scope": "billing:subscribe", "steps": 2, "requires": ["account", "payment"] }, "cancel": { "page": "https://nichedb.dev/account/billing", "api": { "method": "POST", "url": "https://nichedb.dev/api/v1/billing/cancel" }, "scope": "billing:cancel", "steps": 1, "confirm": "click", "effective": "period-end", "refund": "none" }, "unsubscribe": { "page": "https://nichedb.dev/account/mail", "scope": "mail:unsubscribe", "api": { "method": "POST", "url": "https://nichedb.dev/api/v1/mail/unsubscribe" }, "steps": 1, "list_unsubscribe": true }, "export": { "page": "https://nichedb.dev/account/export", "scope": "account:export", "api": { "method": "POST", "url": "https://nichedb.dev/api/v1/account/export" }, "steps": 1, "formats": ["json"], "within": "P1D" }, "delete": { "page": "https://nichedb.dev/account/delete", "scope": "account:delete", "api": { "method": "POST", "url": "https://nichedb.dev/api/v1/account/delete" }, "steps": 2, "confirm": "email", "effective": "immediate", "retention": "P30D" } }, "policies": { "auto_renew": true, "renewal_notice": "P7D", "price_change_notice": "P30D" } }`; const ACTIONS: Array<[string, string, string]> = [ ["subscribe", "start a plan", "requires: account, payment, card, invoice, approval"], ["cancel", "end the plan", "effective: immediate or period-end; refund: none, prorated, full, P14D, or a URL"], ["pause", "stop paying for a while", "max: the longest pause, as a duration"], ["resume", "come back from a pause", ""], ["change_plan", "move to another plan", "proration: immediate, period-end, none"], ["unsubscribe", "stop the mail, not the plan", "list_unsubscribe: true when RFC 8058 headers are in the mail"], ["export", "take your data", "formats, and within: how long it takes"], ["delete", "close the account", "retention: how long the data lives after"] ]; const BOTH: Array<[string, string]> = [ ["page", "The URL a person opens. The human half of the action, and the one place the way out is written down beside the way in."], ["api", "{ method, url } an agent calls with an OpenAccess bearer. Answers done, scheduled, pending with a next, or refused with a reason."], ["scope", "The OpenAccess scope the bearer must carry, defined in the service's own openaccess.json with a line a person reads on the consent screen."], ["steps", "How many things a person does from page to done. cancel.steps against subscribe.steps is the number a directory shows first."], ["confirm", "What the service demands before it takes: none, click, email, password, chat, call, mail. The last three are the ones a directory flags."] ]; const ABSENT: Array<[string, string]> = [ ["No checkout", "subscribe.api starts a plan for a person with a way to pay on file, or answers pending with the page to add one. Payment is the processor's business."], ["No retention offers", "A discount on the way out is offered on page, to a person. cancel.api cancels."], ["No account data", "The file describes actions, not the account. The current plan comes from the service's API under the reserved entitlements scope."], ["No badges", "steps and confirm are the service's own statements. A directory that scores exit ease labels the score as its own and shows the numbers."] ]; export default function OpenSaaSPage(): ReactNode { return (

LogicSRC standards surface

OpenSaaS

One file a subscription service serves about how to deal with it: the way in and the way out of every plan, for a person and for an agent, in the same place.

Subscribing takes one click. Cancelling takes a support ticket, a phone call in office hours and a retention screen. The way in is on the pricing page; the way out is nowhere a search finds, and an agent asked to cancel what a person does not use cannot. The service already has a plan table, a cancel endpoint and a refund rule. OpenSaaS is those, exported, at /.well-known/opensaas.json: every action as the{" "} page a person opens and the api an agent calls with an OpenAccess scope.

Status: 0.1. The first directory reading it is the saas collection at{" "} nichedb.dev, and the first service serving one is nichedb.dev itself. The smallest valid file is a service with a name and one action with a page.

The descriptor

A cancel endpoint fetched from the service's own origin is a cancel endpoint the service says works. That is the whole verification.

{DESCRIPTOR}

Eight actions

unsubscribe is about mail and cancel{" "} is about the plan, and they are two actions because services conflate them on purpose. Unknown actions are kept under their own name.

{ACTIONS.map(([action, what, keys]) => ( ))}
action what it does its own keys
{action} {what} {keys}

One action, both readers

Every action carries the same five keys. An action with a page and no api can only be done by a person; one with an api and no page only by an agent; a directory shows which.

{BOTH.map(([key, what]) => ( ))}
{key} {what}

What is deliberately absent

{ABSENT.map(([what, why]) => ( ))}
{what} {why}

Where everything lives

); }