Files
adaptive-multi-protocol-gat…/ampg/transports.py

199 lines
6.7 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class ProviderSource:
kind: str
discovery: str
lifecycle: str
notes: tuple[str, ...] = ()
@dataclass(frozen=True)
class TransportAdapter:
protocol: str
daemon: str
executable: str
backend: str
adopt_supported: bool
managed_supported: bool
default_ports: tuple[int, ...] = ()
generated_artifacts: tuple[str, ...] = ()
provider_sources: tuple[ProviderSource, ...] = ()
notes: tuple[str, ...] = ()
COMMON_SERVICE_PROVIDER_SOURCES = (
ProviderSource(
kind="configured",
discovery="explicit daemon config and approved generated snippets",
lifecycle="operator-owned unless daemon_policy selects manage",
notes=("Explicit config always wins over automatic discovery.",),
),
ProviderSource(
kind="system-adopted",
discovery="running system daemon, service, or listen endpoint",
lifecycle="operator-owned; AMPG may generate snippets but does not own the service",
notes=("Used when daemon_policy is adopt or auto with a healthy daemon.",),
),
ProviderSource(
kind="system-managed",
discovery="installed daemon binary on the selected platform",
lifecycle="AMPG-owned state and AMPG-named supervisor entry",
notes=("Used when daemon_policy is manage or auto and the platform can supervise it.",),
),
ProviderSource(
kind="platform-package",
discovery="allowlisted package backend such as apt, brew, or pkg",
lifecycle="installed only through reviewed deploy apply package stage",
notes=("Package install is separate from state, supervisor, start, address, and health stages.",),
),
)
USERSPACE_PROVIDER_SOURCES = COMMON_SERVICE_PROVIDER_SOURCES + (
ProviderSource(
kind="bundled-sidecar",
discovery="AMPG package or app resource provider directory",
lifecycle="AMPG-owned process and state",
notes=("Useful for old laptops, single-purpose boxes, and mobile-server shells.",),
),
)
TRANSPORT_ADAPTERS = (
TransportAdapter(
protocol="clearnet",
daemon="nginx",
executable="nginx",
backend="http",
adopt_supported=True,
managed_supported=True,
default_ports=(80, 443),
generated_artifacts=("nginx server block snippet",),
provider_sources=COMMON_SERVICE_PROVIDER_SOURCES,
notes=(
"Clearnet ingress is usually adopted because TLS and public vhost policy are operator-owned.",
),
),
TransportAdapter(
protocol="tor",
daemon="tor",
executable="tor",
backend="hidden-service",
adopt_supported=True,
managed_supported=True,
default_ports=(18080,),
generated_artifacts=("torrc hidden-service snippet", "loopback HTTP server block"),
provider_sources=USERSPACE_PROVIDER_SOURCES,
notes=(
"Adopted Tor keeps existing HiddenServiceDir material.",
"Managed Tor stores onion service keys under AMPG state.",
),
),
TransportAdapter(
protocol="tor",
daemon="arti",
executable="arti",
backend="hidden-service",
adopt_supported=False,
managed_supported=False,
default_ports=(18080,),
generated_artifacts=("future Arti onion-service config",),
provider_sources=(
ProviderSource(
kind="bundled-sidecar",
discovery="AMPG package or app resource provider directory",
lifecycle="future AMPG-owned Arti onion-service process",
),
ProviderSource(
kind="configured",
discovery="explicit arti binary path and onion-service config",
lifecycle="future AMPG-owned or operator-owned by policy",
),
),
notes=("Modeled as a future backend until the AMPG Arti adapter is implemented.",),
),
TransportAdapter(
protocol="i2p",
daemon="i2pd",
executable="i2pd",
backend="server-tunnel",
adopt_supported=True,
managed_supported=True,
default_ports=(18081,),
generated_artifacts=("i2pd server tunnel snippet", "loopback HTTP server block"),
provider_sources=USERSPACE_PROVIDER_SOURCES,
notes=("Destination keys are transport identity material and must be preserved.",),
),
TransportAdapter(
protocol="gemini",
daemon="agate",
executable="agate",
backend="gemini-server",
adopt_supported=True,
managed_supported=True,
default_ports=(1965,),
generated_artifacts=("Agate plan values",),
provider_sources=USERSPACE_PROVIDER_SOURCES,
notes=("Gemini serves generated Gemtext output directly.",),
),
TransportAdapter(
protocol="ipfs",
daemon="ipfs",
executable="ipfs",
backend="content-addressed",
adopt_supported=True,
managed_supported=True,
default_ports=(5001, 8080),
generated_artifacts=("pin/add plan",),
provider_sources=USERSPACE_PROVIDER_SOURCES,
notes=("IPFS is content-addressed distribution, not an anonymity layer.",),
),
TransportAdapter(
protocol="reticulum",
daemon="rnsd",
executable="rnsd",
backend="reticulum-page-service",
adopt_supported=True,
managed_supported=True,
default_ports=(),
generated_artifacts=("Reticulum page-service plan",),
provider_sources=USERSPACE_PROVIDER_SOURCES
+ (
ProviderSource(
kind="operator-interface",
discovery="explicit Reticulum interface configuration",
lifecycle="operator-owned physical or link-layer interface",
notes=("AMPG can manage page serving while interface setup may remain manual.",),
),
),
notes=(
"Reticulum is resilient/private routing, not an anonymity layer.",
"Physical interfaces may require operator setup outside AMPG.",
),
),
)
def transport_adapter(protocol: str, daemon: str) -> TransportAdapter | None:
for adapter in TRANSPORT_ADAPTERS:
if adapter.protocol == protocol and adapter.daemon == daemon:
return adapter
return None
def fallback_transport_adapter(protocol: str, daemon: str) -> TransportAdapter:
return TransportAdapter(
protocol=protocol,
daemon=daemon,
executable=daemon,
backend="unknown",
adopt_supported=False,
managed_supported=False,
generated_artifacts=(),
notes=("No AMPG adapter is registered for this protocol/daemon pair.",),
)