Add IPFS dry-run adapter support

This commit is contained in:
jwinterm
2026-07-05 21:05:46 -04:00
parent a8cb056225
commit 3a51468d29
24 changed files with 169 additions and 33 deletions
+15 -1
View File
@@ -106,10 +106,24 @@ ADAPTERS: dict[str, TransportAdapter] = {
install_strategy="install Reticulum provider",
start_strategy="start configured Reticulum interface",
stop_policy="stop only AMPB-owned Reticulum process",
note="Reticulum adapter is planned; interface readiness needs explicit config",
note="Reticulum adapter is planned for resilient/private routing; it is not an anonymity layer",
commands=("rnsd", "rnstatus", "rnodeconf"),
manage_supported=False,
),
"ipfs": TransportAdapter(
name="ipfs",
profile="ipfs",
endpoint="http://127.0.0.1:8080",
adopt_check="HTTP gateway on 127.0.0.1:8080",
managed_state=".ampb/transports/ipfs",
install_strategy="install IPFS/Kubo provider",
start_strategy="start managed IPFS gateway",
stop_policy="stop only AMPB-owned IPFS process",
note="IPFS local gateway for content-addressed distribution; not an anonymity layer",
commands=("ipfs",),
connect_host="127.0.0.1",
connect_port=8080,
),
"gemini": TransportAdapter(
name="gemini",
profile="gemini",
+1 -1
View File
@@ -20,7 +20,7 @@ CANDIDATE_TRANSPORTS = (
CandidateTransport(
name="ipfs",
role="content-addressed web",
status="next-evaluate",
status="active-dry-run",
route_examples=("ipfs://<cid>", "/ipfs/<cid>", "ipns://<name>"),
browser_fit="native fetch or local gateway",
publisher_fit="static site export and pinning",
+10 -2
View File
@@ -23,8 +23,9 @@ class TransportDefinition:
ROUTE_RULES = (
RouteRule("*.onion", "tor", "tor", "route through Tor SOCKS"),
RouteRule("*.i2p", "i2p", "i2p", "route through I2P HTTP proxy"),
RouteRule("ipfs://*, ipns://*, /ipfs/*, /ipns/*", "ipfs", "ipfs", "route through local IPFS gateway"),
RouteRule("gemini://*", "gemini", "gemini", "fetch and render Gemtext"),
RouteRule("rns://*, lxmf://*, nomad://*", "reticulum", "reticulum", "route through Reticulum adapter"),
RouteRule("rns://*, lxmf://*, nomad://*", "reticulum", "reticulum", "route to Reticulum adapter"),
RouteRule("http://*, https://*", "clearnet", "clearnet", "ordinary web profile"),
)
@@ -43,6 +44,13 @@ TRANSPORT_DEFINITIONS = (
"i2p",
"Adopt existing i2pd/I2P router when healthy; otherwise prompt before managed setup.",
),
TransportDefinition(
"ipfs",
"HTTP gateway on 127.0.0.1:8080",
".ampb/transports/ipfs",
"ipfs",
"Adopt existing IPFS gateway when healthy; otherwise prompt before managed setup.",
),
TransportDefinition(
"gemini",
"built-in renderer available",
@@ -55,6 +63,6 @@ TRANSPORT_DEFINITIONS = (
"RNS tools and configured interfaces",
".ampb/transports/reticulum",
"reticulum",
"Adapter planned; physical/link-layer setup may require operator config.",
"Adapter planned for resilient/private routing; physical/link-layer setup may require operator config.",
),
)
+5 -2
View File
@@ -33,11 +33,14 @@ PLATFORM_CAPABILITIES = (
PlatformCapability("i2p", "desktop", "ready", "ready", "planned", "planned", "Adopt local I2P proxy or prompt before managed desktop daemon setup."),
PlatformCapability("i2p", "android", "planned", "planned", "planned", "planned", "Use an app-owned foreground service or compatible installed I2P provider."),
PlatformCapability("i2p", "ios", "constrained", "planned", "foreground-only", "constrained", "Treat iOS I2P support as foreground-only until native adapter constraints are proven."),
PlatformCapability("ipfs", "desktop", "ready", "ready", "planned", "planned", "Adopt local IPFS gateway or prompt before managed gateway setup."),
PlatformCapability("ipfs", "android", "planned", "planned", "planned", "planned", "Use gateway-first IPFS browsing; embedded node support needs adapter proof."),
PlatformCapability("ipfs", "ios", "constrained", "planned", "foreground-only", "constrained", "Use gateway-first IPFS browsing; embedded node support is constrained."),
PlatformCapability("gemini", "desktop", "ready", "ready", "ready", "unsupported", "Built-in fetch/render path can be shared by desktop shell."),
PlatformCapability("gemini", "android", "ready", "ready", "ready", "unsupported", "Built-in fetch/render path can be shared by Android shell."),
PlatformCapability("gemini", "ios", "ready", "ready", "ready", "unsupported", "Built-in fetch/render path can be shared by iOS shell."),
PlatformCapability("reticulum", "desktop", "planned", "planned", "planned", "planned", "Adapter and local interface readiness checks are planned."),
PlatformCapability("reticulum", "android", "planned", "planned", "planned", "planned", "Mobile adapter should use explicit foreground service consent."),
PlatformCapability("reticulum", "desktop", "planned", "planned", "planned", "planned", "Resilient/private networking adapter and interface checks are planned."),
PlatformCapability("reticulum", "android", "planned", "planned", "planned", "planned", "Mobile resilient-network adapter should use explicit foreground service consent."),
PlatformCapability("reticulum", "ios", "constrained", "planned", "foreground-only", "constrained", "Do not assume background Reticulum operation on iOS."),
PlatformCapability("unknown", "desktop", "unsupported", "unsupported", "unsupported", "unsupported", "No platform route is available."),
PlatformCapability("unknown", "android", "unsupported", "unsupported", "unsupported", "unsupported", "No platform route is available."),
+20 -1
View File
@@ -14,6 +14,10 @@ class Route:
def route_url(raw_url: str) -> Route:
path_route = _route_path_address(raw_url)
if path_route:
return path_route
normalized = _normalize_url(raw_url)
parsed = urlparse(normalized)
scheme = parsed.scheme.lower()
@@ -21,6 +25,8 @@ def route_url(raw_url: str) -> Route:
if scheme == "gemini":
return Route(raw_url, normalized, "gemini", "gemini", "gemini scheme")
if scheme in {"ipfs", "ipns"}:
return Route(raw_url, normalized, "ipfs", "ipfs", f"{scheme} scheme")
if scheme in {"rns", "lxmf", "nomad"}:
return Route(raw_url, normalized, "reticulum", "reticulum", f"{scheme} scheme")
if host.endswith(".onion"):
@@ -34,6 +40,19 @@ def route_url(raw_url: str) -> Route:
def _normalize_url(raw_url: str) -> str:
value = raw_url.strip()
if "://" not in value and not value.startswith(("rns:", "lxmf:")):
if "://" not in value and not value.startswith(("rns:", "lxmf:", "ipfs:", "ipns:")):
value = "https://" + value
return value
def _route_path_address(raw_url: str) -> Route | None:
value = raw_url.strip()
if value.startswith("/ipfs/"):
cid = value.removeprefix("/ipfs/").strip("/")
normalized = f"ipfs://{cid}" if cid else "ipfs://"
return Route(raw_url, normalized, "ipfs", "ipfs", "/ipfs path")
if value.startswith("/ipns/"):
name = value.removeprefix("/ipns/").strip("/")
normalized = f"ipns://{name}" if name else "ipns://"
return Route(raw_url, normalized, "ipfs", "ipfs", "/ipns path")
return None