Add interaction capability policy docs

This commit is contained in:
jwinterm
2026-07-06 03:20:05 -04:00
parent 717c8fb9c5
commit 49054f729b
9 changed files with 451 additions and 83 deletions
+1
View File
@@ -79,6 +79,7 @@ internal even when the public site is available on Tor or I2P.
- [Configuration](docs/configuration.md) - [Configuration](docs/configuration.md)
- [Daemon management](docs/daemon-management.md) - [Daemon management](docs/daemon-management.md)
- [Fixture manifests](docs/fixture-manifests.md) - [Fixture manifests](docs/fixture-manifests.md)
- [Interaction capabilities](docs/interaction-capabilities.md)
- [Interactive applications](docs/interactive-apps.md) - [Interactive applications](docs/interactive-apps.md)
- [Wownero integration](docs/wownero-integration.md) - [Wownero integration](docs/wownero-integration.md)
- [Autodocs and drift gates](docs/autodocs.md) - [Autodocs and drift gates](docs/autodocs.md)
+55 -1
View File
@@ -2,7 +2,15 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from .metadata import CONFIG_FIELDS, DAEMON_ADAPTERS, RENDER_PROFILES from .metadata import (
CONFIG_FIELDS,
DAEMON_ADAPTERS,
IDENTITY_ADAPTERS,
INTERACTION_TIERS,
PAYMENT_ADAPTERS,
RENDER_PROFILES,
TRANSPORT_INTERACTION_CAPABILITIES,
)
GENERATED_DIR = Path("docs/generated") GENERATED_DIR = Path("docs/generated")
@@ -12,6 +20,7 @@ def generate_docs(root: Path, *, check: bool = False) -> list[Path]:
docs = { docs = {
GENERATED_DIR / "config-schema.md": _config_schema_doc(), GENERATED_DIR / "config-schema.md": _config_schema_doc(),
GENERATED_DIR / "daemon-adapters.md": _daemon_adapters_doc(), GENERATED_DIR / "daemon-adapters.md": _daemon_adapters_doc(),
GENERATED_DIR / "interaction-capabilities.md": _interaction_capabilities_doc(),
GENERATED_DIR / "render-profiles.md": _render_profiles_doc(), GENERATED_DIR / "render-profiles.md": _render_profiles_doc(),
} }
changed: list[Path] = [] changed: list[Path] = []
@@ -83,3 +92,48 @@ def _render_profiles_doc() -> str:
sections.append(f"- {default}") sections.append(f"- {default}")
sections.append("") sections.append("")
return "\n".join(sections) return "\n".join(sections)
def _interaction_capabilities_doc() -> str:
sections = [_header("Generated Interaction Capabilities")]
sections.append("## Tiers\n")
sections.append("| Tier | Summary | Examples | Notes |")
sections.append("| --- | --- | --- | --- |")
for tier in INTERACTION_TIERS:
sections.append(
f"| `{tier.name}` | {tier.summary} | {', '.join(tier.examples)} | "
f"{' '.join(tier.notes)} |"
)
sections.append("")
sections.append("## Identity Adapters\n")
sections.append("| Adapter | Status | Transports | Notes |")
sections.append("| --- | --- | --- | --- |")
for adapter in IDENTITY_ADAPTERS:
sections.append(
f"| `{adapter.name}` | `{adapter.status}` | {', '.join(adapter.transports)} | "
f"{' '.join(adapter.notes)} |"
)
sections.append("")
sections.append("## Payment Adapters\n")
sections.append("| Adapter | Status | Transports | Notes |")
sections.append("| --- | --- | --- | --- |")
for adapter in PAYMENT_ADAPTERS:
sections.append(
f"| `{adapter.name}` | `{adapter.status}` | {', '.join(adapter.transports)} | "
f"{' '.join(adapter.notes)} |"
)
sections.append("")
sections.append("## Transport Limits\n")
sections.append("| Transport | Public Max Tier | Identity | Payments | Realtime | Notes |")
sections.append("| --- | --- | --- | --- | --- | --- |")
for capability in TRANSPORT_INTERACTION_CAPABILITIES:
sections.append(
f"| `{capability.transport}` | `{capability.public_max_tier}` | "
f"{capability.identity} | {capability.payments} | {capability.realtime} | "
f"{' '.join(capability.notes)} |"
)
sections.append("")
return "\n".join(sections)
+235
View File
@@ -33,6 +33,40 @@ class DaemonAdapter:
notes: tuple[str, ...] notes: tuple[str, ...]
@dataclass(frozen=True)
class InteractionTier:
name: str
summary: str
examples: tuple[str, ...]
notes: tuple[str, ...]
@dataclass(frozen=True)
class IdentityAdapter:
name: str
status: str
transports: tuple[str, ...]
notes: tuple[str, ...]
@dataclass(frozen=True)
class PaymentAdapter:
name: str
status: str
transports: tuple[str, ...]
notes: tuple[str, ...]
@dataclass(frozen=True)
class TransportInteractionCapability:
transport: str
public_max_tier: str
identity: str
payments: str
realtime: str
notes: tuple[str, ...]
CONFIG_FIELDS = ( CONFIG_FIELDS = (
ConfigField("site", "id", "string", True, "", "Stable site identifier used in output paths."), ConfigField("site", "id", "string", True, "", "Stable site identifier used in output paths."),
ConfigField("site", "domain", "string", True, "", "Canonical public domain for the site."), ConfigField("site", "domain", "string", True, "", "Canonical public domain for the site."),
@@ -57,6 +91,38 @@ CONFIG_FIELDS = (
"[]", "[]",
"Route patterns that must not be published.", "Route patterns that must not be published.",
), ),
ConfigField(
"site.interactions.route",
"match",
"glob",
False,
"",
"Route pattern for an explicit interaction policy.",
),
ConfigField(
"site.interactions.route",
"tier",
"enum",
False,
"site.interactions.default_tier",
"Interaction tier for a route group.",
),
ConfigField(
"site.interactions.route",
"identity",
"enum",
False,
"none",
"Identity adapter required by a route group.",
),
ConfigField(
"site.interactions.route",
"payments",
"enum",
False,
"none",
"Payment adapter required by a route group.",
),
ConfigField( ConfigField(
"site.protocols.<name>", "site.protocols.<name>",
"enabled", "enabled",
@@ -108,6 +174,175 @@ CONFIG_FIELDS = (
) )
INTERACTION_TIERS = (
InteractionTier(
name="static",
summary="Rendered files only.",
examples=("documentation", "landing pages", "catalog pages"),
notes=("Works across every currently modeled transport.",),
),
InteractionTier(
name="interactive-lite",
summary="Client-visible state with deterministic or server-rendered updates.",
examples=("deterministic games", "leaderboards", "quote forms", "status lookups"),
notes=(
"Does not require accounts or payment confirmation.",
"Constrained transports may receive static snapshots or form-style actions.",
),
),
InteractionTier(
name="identity",
summary="Authenticated sessions or signed wallet identity.",
examples=("account pages", "wallet sign-in", "claim pages"),
notes=(
"Requires explicit identity adapter selection.",
"HTTP transports can use strict cookies; wallet sign-in uses signed challenges.",
),
),
InteractionTier(
name="transactional",
summary="Server-confirmed orders, wagers, deposits, invoices, or payment intents.",
examples=("basic stores", "paid downloads", "game entry fees", "donations"),
notes=(
"Requires explicit payment adapter selection.",
"Callbacks, webhook receivers, and ledger internals remain internal.",
),
),
InteractionTier(
name="realtime",
summary="Live multiplayer, dashboards, websocket streams, or fast state sync.",
examples=("multiplayer games", "live markets", "operator consoles"),
notes=(
"HTTP transports only in the current model.",
"Usually clearnet or private Tor/I2P until transport-specific realtime support exists.",
),
),
InteractionTier(
name="internal",
summary="Private/admin/worker surfaces that must not be published automatically.",
examples=("admin panels", "webhooks", "worker APIs", "health endpoints"),
notes=("Always deny by default on public outputs.",),
),
)
IDENTITY_ADAPTERS = (
IdentityAdapter(
name="none",
status="ready",
transports=("clearnet", "tor", "i2p", "gemini", "ipfs", "reticulum"),
notes=("No account or signed identity required.",),
),
IdentityAdapter(
name="http-session",
status="planned",
transports=("clearnet", "tor", "i2p"),
notes=(
"Use HTTP-only cookies and strict transport-specific session scope.",
"Profiles must not be shared across transports.",
),
),
IdentityAdapter(
name="siwe",
status="planned",
transports=("clearnet", "tor", "i2p"),
notes=(
"Sign-In with Ethereum style challenge/response.",
"Wallet transport availability is a browser-shell concern.",
),
),
IdentityAdapter(
name="signed-link",
status="research",
transports=("gemini", "reticulum"),
notes=("Use only for narrow flows with short-lived, scoped capabilities.",),
),
)
PAYMENT_ADAPTERS = (
PaymentAdapter(
name="none",
status="ready",
transports=("clearnet", "tor", "i2p", "gemini", "ipfs", "reticulum"),
notes=("No payment required.",),
),
PaymentAdapter(
name="server-invoice",
status="planned",
transports=("clearnet", "tor", "i2p"),
notes=(
"Server creates invoice or payment intent and confirms settlement.",
"Callback/webhook routes stay internal.",
),
),
PaymentAdapter(
name="wallet-signature",
status="planned",
transports=("clearnet", "tor", "i2p"),
notes=("Browser wallet signs a request; server verifies before fulfilling.",),
),
PaymentAdapter(
name="static-instructions",
status="planned",
transports=("gemini", "ipfs", "reticulum"),
notes=("Render payment instructions or donation addresses without automatic fulfillment.",),
),
)
TRANSPORT_INTERACTION_CAPABILITIES = (
TransportInteractionCapability(
transport="clearnet",
public_max_tier="realtime",
identity="http-session, siwe",
payments="server-invoice, wallet-signature",
realtime="yes",
notes=("Highest-fidelity browser surface.",),
),
TransportInteractionCapability(
transport="tor",
public_max_tier="transactional",
identity="http-session, siwe",
payments="server-invoice, wallet-signature",
realtime="private-only",
notes=("Prefer server-rendered flows and reduced JavaScript by default.",),
),
TransportInteractionCapability(
transport="i2p",
public_max_tier="transactional",
identity="http-session, siwe",
payments="server-invoice, wallet-signature",
realtime="private-only",
notes=("Prefer server-rendered flows and reduced JavaScript by default.",),
),
TransportInteractionCapability(
transport="gemini",
public_max_tier="interactive-lite",
identity="signed-link research",
payments="static-instructions",
realtime="no",
notes=("Use curated prompts or polling-friendly status pages.",),
),
TransportInteractionCapability(
transport="ipfs",
public_max_tier="static",
identity="none",
payments="static-instructions",
realtime="no",
notes=("Content-addressed snapshots; no server-confirmed state by default.",),
),
TransportInteractionCapability(
transport="reticulum",
public_max_tier="interactive-lite",
identity="signed-link research",
payments="static-instructions",
realtime="research",
notes=("Resilient/private routing; not an anonymity layer.",),
),
)
RENDER_PROFILES = ( RENDER_PROFILES = (
RenderProfile( RenderProfile(
name="clearnet", name="clearnet",
+12 -4
View File
@@ -31,6 +31,12 @@ plan_root = "/var/lib/ampg/plans"
default_tier = "static" default_tier = "static"
deny_routes = ["/admin/*", "/internal/*", "/webhooks/*"] deny_routes = ["/admin/*", "/internal/*", "/webhooks/*"]
[[site.interactions.route]]
match = "/checkout/*"
tier = "transactional"
identity = "http-session"
payments = "server-invoice"
[site.protocols.clearnet] [site.protocols.clearnet]
enabled = true enabled = true
renderer = "clearnet" renderer = "clearnet"
@@ -151,7 +157,7 @@ upstream = "http://127.0.0.1:8000"
openapi = "http://127.0.0.1:9000/openapi.json" openapi = "http://127.0.0.1:9000/openapi.json"
[site.interactions] [site.interactions]
default_tier = "forms" default_tier = "interactive-lite"
session_policy = "http-only-cookie" session_policy = "http-only-cookie"
deny_routes = ["/admin/*", "/api/internal/*", "/webhooks/*"] deny_routes = ["/admin/*", "/api/internal/*", "/webhooks/*"]
@@ -161,11 +167,13 @@ tier = "internal"
[[site.interactions.route]] [[site.interactions.route]]
match = "/checkout/*" match = "/checkout/*"
tier = "sessions" tier = "transactional"
identity = "http-session"
payments = "server-invoice"
[[site.interactions.route]] [[site.interactions.route]]
match = "/catalog/*" match = "/catalog/*"
tier = "forms" tier = "interactive-lite"
[site.protocols.tor] [site.protocols.tor]
enabled = true enabled = true
@@ -177,7 +185,7 @@ allow_javascript = false
enabled = true enabled = true
renderer = "gemtext" renderer = "gemtext"
daemon_policy = "auto" daemon_policy = "auto"
max_tier = "forms" max_tier = "interactive-lite"
``` ```
`max_tier` is a hard cap. If a route needs a higher tier than the protocol allows, AMPG `max_tier` is a hard cap. If a route needs a higher tier than the protocol allows, AMPG
+4
View File
@@ -15,6 +15,10 @@ This file is generated from code. Do not edit it by hand.
| site.outputs | `plan_root` | path | no | ../dist/ampg-plan | Generated plan artifact root. | | site.outputs | `plan_root` | path | no | ../dist/ampg-plan | Generated plan artifact root. |
| site.interactions | `default_tier` | enum | no | static | Default interaction tier for routes. | | site.interactions | `default_tier` | enum | no | static | Default interaction tier for routes. |
| site.interactions | `deny_routes` | array<string> | no | [] | Route patterns that must not be published. | | site.interactions | `deny_routes` | array<string> | no | [] | Route patterns that must not be published. |
| site.interactions.route | `match` | glob | no | - | Route pattern for an explicit interaction policy. |
| site.interactions.route | `tier` | enum | no | site.interactions.default_tier | Interaction tier for a route group. |
| site.interactions.route | `identity` | enum | no | none | Identity adapter required by a route group. |
| site.interactions.route | `payments` | enum | no | none | Payment adapter required by a route group. |
| site.protocols.<name> | `enabled` | boolean | no | false | Whether this protocol target is built and planned. | | site.protocols.<name> | `enabled` | boolean | no | false | Whether this protocol target is built and planned. |
| site.protocols.<name> | `renderer` | enum | no | <protocol> | Renderer profile to use for this protocol. | | site.protocols.<name> | `renderer` | enum | no | <protocol> | Renderer profile to use for this protocol. |
| site.protocols.<name> | `daemon` | enum | no | protocol default | Ingress daemon adapter. | | site.protocols.<name> | `daemon` | enum | no | protocol default | Ingress daemon adapter. |
@@ -0,0 +1,46 @@
# Generated Interaction Capabilities
> Status: generated | Updated by `python3 -m ampg docs generate` | Applies to: AMPG
This file is generated from code. Do not edit it by hand.
## Tiers
| Tier | Summary | Examples | Notes |
| --- | --- | --- | --- |
| `static` | Rendered files only. | documentation, landing pages, catalog pages | Works across every currently modeled transport. |
| `interactive-lite` | Client-visible state with deterministic or server-rendered updates. | deterministic games, leaderboards, quote forms, status lookups | Does not require accounts or payment confirmation. Constrained transports may receive static snapshots or form-style actions. |
| `identity` | Authenticated sessions or signed wallet identity. | account pages, wallet sign-in, claim pages | Requires explicit identity adapter selection. HTTP transports can use strict cookies; wallet sign-in uses signed challenges. |
| `transactional` | Server-confirmed orders, wagers, deposits, invoices, or payment intents. | basic stores, paid downloads, game entry fees, donations | Requires explicit payment adapter selection. Callbacks, webhook receivers, and ledger internals remain internal. |
| `realtime` | Live multiplayer, dashboards, websocket streams, or fast state sync. | multiplayer games, live markets, operator consoles | HTTP transports only in the current model. Usually clearnet or private Tor/I2P until transport-specific realtime support exists. |
| `internal` | Private/admin/worker surfaces that must not be published automatically. | admin panels, webhooks, worker APIs, health endpoints | Always deny by default on public outputs. |
## Identity Adapters
| Adapter | Status | Transports | Notes |
| --- | --- | --- | --- |
| `none` | `ready` | clearnet, tor, i2p, gemini, ipfs, reticulum | No account or signed identity required. |
| `http-session` | `planned` | clearnet, tor, i2p | Use HTTP-only cookies and strict transport-specific session scope. Profiles must not be shared across transports. |
| `siwe` | `planned` | clearnet, tor, i2p | Sign-In with Ethereum style challenge/response. Wallet transport availability is a browser-shell concern. |
| `signed-link` | `research` | gemini, reticulum | Use only for narrow flows with short-lived, scoped capabilities. |
## Payment Adapters
| Adapter | Status | Transports | Notes |
| --- | --- | --- | --- |
| `none` | `ready` | clearnet, tor, i2p, gemini, ipfs, reticulum | No payment required. |
| `server-invoice` | `planned` | clearnet, tor, i2p | Server creates invoice or payment intent and confirms settlement. Callback/webhook routes stay internal. |
| `wallet-signature` | `planned` | clearnet, tor, i2p | Browser wallet signs a request; server verifies before fulfilling. |
| `static-instructions` | `planned` | gemini, ipfs, reticulum | Render payment instructions or donation addresses without automatic fulfillment. |
## Transport Limits
| Transport | Public Max Tier | Identity | Payments | Realtime | Notes |
| --- | --- | --- | --- | --- | --- |
| `clearnet` | `realtime` | http-session, siwe | server-invoice, wallet-signature | yes | Highest-fidelity browser surface. |
| `tor` | `transactional` | http-session, siwe | server-invoice, wallet-signature | private-only | Prefer server-rendered flows and reduced JavaScript by default. |
| `i2p` | `transactional` | http-session, siwe | server-invoice, wallet-signature | private-only | Prefer server-rendered flows and reduced JavaScript by default. |
| `gemini` | `interactive-lite` | signed-link research | static-instructions | no | Use curated prompts or polling-friendly status pages. |
| `ipfs` | `static` | none | static-instructions | no | Content-addressed snapshots; no server-confirmed state by default. |
| `reticulum` | `interactive-lite` | signed-link research | static-instructions | research | Resilient/private routing; not an anonymity layer. |
+24
View File
@@ -0,0 +1,24 @@
# Interaction Capabilities
> Status: draft | Updated 2026-07-06 | Applies to: AMPG route exposure policy
Interaction capabilities define what AMPG may expose for dynamic applications. They are
public, generic rules. Project strategy, target names, and implementation notes belong in
`docs/private/`.
## Generated Policy
The code-owned policy tables live in
[generated interaction capabilities](generated/interaction-capabilities.md).
They cover:
- interaction tiers from `static` through `internal`.
- identity adapters such as HTTP sessions and wallet challenge/response.
- payment adapters such as server invoices and wallet-signed requests.
- per-transport limits for clearnet, Tor, I2P, Gemini, IPFS, and Reticulum.
## Rule
AMPG only exposes a route when the route tier, identity adapter, payment adapter, and
transport capability all agree. When in doubt, the route stays private.
+44 -78
View File
@@ -1,39 +1,43 @@
# Interactive Applications # Interactive Applications
> Status: draft | Updated 2026-07-05 | Applies to: dynamic app support > Status: draft | Updated 2026-07-06 | Applies to: dynamic app support
AMPG must support real applications without pretending every transport can offer the same AMPG must support real applications without pretending every transport can offer the same
experience. The design goal is graceful capability mapping: expose the safest useful version experience. The design goal is graceful capability mapping: expose the safest useful
of each surface on each protocol, and keep private surfaces private. version of each public surface on each protocol, and keep private surfaces private.
## Interaction tiers ## Interaction Tiers
`static` `static`
: Rendered files only. Works everywhere. : Rendered files only. Works everywhere.
`forms` `interactive-lite`
: Server-rendered GET/POST actions with full-page responses. Works on clearnet, Tor, I2P, : Deterministic games, status lookups, quote forms, and other flows that do not require
and can be mapped to Gemini input prompts or Reticulum page scripts for simple actions. account state or payment confirmation.
`sessions` `identity`
: Authenticated account flows. Supported on HTTP transports with strict cookies. Gemini can : Authenticated sessions, wallet sign-in, or signed capability links.
use client certificates or signed links later, but this is not a v1 requirement.
`transactional`
: Server-confirmed orders, wagers, deposits, invoices, donations, or payment intents.
`realtime` `realtime`
: Rich browser UI, dashboards, live charts, websockets, or client-side state. HTTP transports : Multiplayer, dashboards, websockets, streaming updates, or fast shared state.
only, and usually clearnet or private Tor/I2P.
`internal` `internal`
: Worker APIs, webhooks, private admin APIs, payment callbacks, chain watchers, and service : Admin, worker APIs, webhooks, callback endpoints, chain watchers, health checks, and
health endpoints. AMPG must not publish these automatically. private operator surfaces. AMPG must not publish these automatically.
## Application adapter contract The code-owned capability matrix lives in
[generated interaction capabilities](generated/interaction-capabilities.md).
## Application Adapter Contract
An application adapter describes what AMPG may expose: An application adapter describes what AMPG may expose:
- upstream service and health check. - upstream service and health check.
- OpenAPI spec or route manifest when available. - OpenAPI spec or route manifest when available.
- route groups with interaction tier, auth requirement, and idempotency behavior. - route groups with interaction tier, auth requirement, payment requirement, and idempotency behavior.
- form/action schemas for protocol translation. - form/action schemas for protocol translation.
- public asset and cache policy. - public asset and cache policy.
- private route denylist. - private route denylist.
@@ -41,83 +45,45 @@ An application adapter describes what AMPG may expose:
Adapters do not bypass the application. They translate constrained protocol input into the Adapters do not bypass the application. They translate constrained protocol input into the
application's public action surface, then render safe responses. application's public action surface, then render safe responses.
## Protocol mapping ## Protocol Mapping
| Tier | Clearnet | Tor/I2P | Gemini | Reticulum | | Tier | Clearnet | Tor/I2P | Gemini | IPFS | Reticulum |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `static` | yes | yes | yes | yes | | `static` | yes | yes | yes | yes | yes |
| `forms` | yes | yes, no JS by default | simple single-field prompts | simple scripts/actions | | `interactive-lite` | yes | yes | curated prompts/status pages | static snapshots only | curated actions |
| `sessions` | yes | yes, strict cookies | later | later | | `identity` | yes | yes, strict sessions | research | no | research |
| `realtime` | yes | optional private surface | no | no | | `transactional` | yes | yes, explicit adapter | instructions/status only | instructions only | instructions/status only |
| `internal` | no public publish | no public publish | no | no | | `realtime` | yes | private/opt-in only | no | no | research |
| `internal` | no public publish | no public publish | no | no | no |
## Challenge profile: Medusa multi-tenant platform ## Identity And Payments
Source: `../medusa-multi-tenant-platform`. Identity and payments are optional adapters, not default site behavior. A route group must
explicitly request them before AMPG exposes account or payment flows on any transport.
Why it matters: Supported design tracks:
- multi-tenant domain routing. - strict HTTP sessions for clearnet, Tor, and I2P.
- storefront, tenant admin, backend, worker, database, Redis, S3/MinIO. - Sign-In with Ethereum style challenge/response for HTTP transports.
- cart, checkout, orders, bookings, blogs, email, payment providers, and webhooks. - server-created invoices or payment intents with internal callbacks.
- tenant-specific themes and custom domains. - wallet-signed requests verified by the upstream app.
- static payment instructions for read-only or constrained outputs.
AMPG support target: Payment callbacks, webhooks, ledger internals, worker routes, and private reconciliation
jobs remain `internal` even when public checkout or donation pages are exposed.
- Clearnet: adopt existing Caddy/reverse-proxy deployment. ## Design Constraints
- Tor/I2P: expose storefront routes with `forms` or `sessions` tier, no JavaScript by default.
- Gemini/Reticulum: expose catalog, blog, policy, product detail, and inquiry/request flows.
- Admin: `internal` unless explicitly bound to a private operator transport.
- Webhooks, payment callbacks, worker queues, health internals: always `internal`.
Required adapter capabilities:
- read tenant/domain registry or accept an exported tenant manifest.
- generate per-tenant protocol targets and daemon plans.
- cap checkout/payment routes by protocol.
- use OpenAPI route metadata where available.
- never expose tenant admin or backend admin by wildcard.
## Challenge profile: evaluetron / ai-gen-bot
Source: `../ai-gen-bot`.
Why it matters:
- OpenAPI-first core.
- customer storefront plus operator console.
- auth, payments, credit ledger, job queue, workers, moderation, and generated artifacts.
- public web channel, Telegram channel, internal worker APIs, and admin APIs.
AMPG support target:
- Clearnet: public storefront and docs.
- Tor/I2P: public storefront with server-rendered account/job flows when enabled.
- Gemini/Reticulum: catalog browsing, deposit instructions, simple generation request, and job
status lookup only after explicit adapter support.
- Operator console: private clearnet, LAN, mesh, or private onion only.
- Worker/internal routes: never published.
Required adapter capabilities:
- consume `api/openapi.yaml` as the action contract.
- map job creation to idempotent form submissions.
- render job status as polling-friendly pages.
- hide unavailable generators when no worker advertises the required capability.
- preserve the ledger and payment invariants of the upstream app.
## Design constraints learned from the challenge apps
- Public route discovery must start deny-first. - Public route discovery must start deny-first.
- OpenAPI is useful, but not sufficient; route tiers and privacy intent must be explicit. - OpenAPI is useful, but not sufficient; route tiers and privacy intent must be explicit.
- Admin and worker surfaces are separate sites or route groups, not accidental subpaths. - Admin and worker surfaces are separate sites or route groups, not accidental subpaths.
- Gemini and Reticulum need curated actions, not automatic exposure of every POST route. - Gemini, IPFS, and Reticulum need curated outputs, not automatic exposure of every POST route.
- Multi-tenant apps need generated protocol targets per tenant, with shared daemon adapters. - Multi-tenant apps need generated protocol targets per tenant, with shared daemon adapters.
## Reusable checklist ## Reusable Checklist
- [ ] Classify every route group by interaction tier. - [ ] Classify every route group by interaction tier.
- [ ] Deny private/admin/internal routes before enabling protocols. - [ ] Deny private/admin/internal routes before enabling protocols.
- [ ] Prefer OpenAPI or route manifests over HTML scraping for actions. - [ ] Prefer OpenAPI or route manifests over HTML scraping for actions.
- [ ] Provide safe alternates for constrained transports. - [ ] Provide safe alternates for constrained transports.
- [ ] Verify no worker, webhook, payment callback, or admin route is publicly published. - [ ] Verify no worker, webhook, payment callback, ledger internal, or admin route is publicly published.
+30
View File
@@ -0,0 +1,30 @@
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
PRIVATE_TARGET_TERMS = (
"wowrace",
"wowngeon",
"smirk-monorepo",
"medusa-multi-tenant-platform",
"ai-gen-bot",
)
class PublicPrivateSplitTest(unittest.TestCase):
def test_private_target_names_do_not_appear_in_public_docs(self):
failures = []
for path in sorted((ROOT / "docs").rglob("*.md")):
rel = path.relative_to(ROOT)
if "private" in rel.parts:
continue
text = path.read_text(encoding="utf-8").lower()
for term in PRIVATE_TARGET_TERMS:
if term in text:
failures.append(f"{rel}: contains private target term {term!r}")
self.assertEqual([], failures)
if __name__ == "__main__":
unittest.main()