fleetmesh-node 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
- fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
- fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
- fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
- fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
- fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
- node/RELEASE +1 -0
- node/__init__.py +35 -0
- node/app/__init__.py +49 -0
- node/app/commands.py +265 -0
- node/app/config.py +131 -0
- node/app/control.py +670 -0
- node/app/core.py +867 -0
- node/app/plugins.py +76 -0
- node/app/router.py +83 -0
- node/app/socket.py +431 -0
- node/app/systemd.py +36 -0
- node/app/views.py +204 -0
- node/channels/__init__.py +378 -0
- node/channels/bus.py +331 -0
- node/channels/journal.py +46 -0
- node/commands/__init__.py +140 -0
- node/commands/adapter.py +210 -0
- node/commands/base.py +278 -0
- node/commands/cluster_cmds.py +183 -0
- node/commands/compute_cmds.py +401 -0
- node/commands/core.py +474 -0
- node/commands/gossip_cmds.py +106 -0
- node/commands/nsite_cmds.py +141 -0
- node/commands/peering.py +402 -0
- node/commands/plugin_cmds.py +135 -0
- node/commands/sidecar_cmds.py +120 -0
- node/commands/sync.py +388 -0
- node/commands/transport_cmds.py +451 -0
- node/commands/video_cmds.py +99 -0
- node/compartment/__init__.py +124 -0
- node/compartment/base.py +868 -0
- node/compartment/command.py +458 -0
- node/compartment/context.py +163 -0
- node/compartment/every.py +108 -0
- node/compartment/handler.py +131 -0
- node/compartment/lifecycle.py +460 -0
- node/compartment/lint.py +388 -0
- node/compartment/need.py +158 -0
- node/compartment/problem.py +521 -0
- node/compartment/refusals.py +120 -0
- node/compartment/registry.py +790 -0
- node/compartment/schema.py +290 -0
- node/compartment/sidecar.py +499 -0
- node/compartment/stores.py +662 -0
- node/compartment/wire.py +225 -0
- node/compartment/worker.py +61 -0
- node/config/__init__.py +43 -0
- node/config/base.py +116 -0
- node/config/change.py +441 -0
- node/config/changestore.py +201 -0
- node/config/compartments.py +1144 -0
- node/config/node.py +318 -0
- node/config/parameter.py +563 -0
- node/config/protocols.py +274 -0
- node/config/records.py +171 -0
- node/examples/__init__.py +1 -0
- node/examples/demo.py +226 -0
- node/examples/follower.py +46 -0
- node/interfaces.py +235 -0
- node/keystore/__init__.py +27 -0
- node/keystore/core.py +103 -0
- node/keystore/enclave.py +688 -0
- node/meshnode.py +111 -0
- node/metrics/__init__.py +396 -0
- node/metrics/adapters.py +102 -0
- node/metrics/face.py +110 -0
- node/planes/__init__.py +21 -0
- node/planes/identity/__init__.py +20 -0
- node/planes/identity/announce/__init__.py +9 -0
- node/planes/identity/announce/compartment.py +447 -0
- node/planes/identity/announce/types.py +26 -0
- node/planes/identity/discovery/__init__.py +9 -0
- node/planes/identity/discovery/compartment.py +522 -0
- node/planes/identity/discovery/types.py +13 -0
- node/planes/identity/linked/__init__.py +1 -0
- node/planes/identity/linked/compartment.py +270 -0
- node/planes/identity/linked/types.py +25 -0
- node/planes/identity/presence/__init__.py +7 -0
- node/planes/identity/presence/compartment.py +409 -0
- node/planes/identity/presence/types.py +14 -0
- node/planes/operations/__init__.py +13 -0
- node/planes/operations/scheduler/__init__.py +9 -0
- node/planes/operations/scheduler/compartment.py +353 -0
- node/planes/operations/scheduler/types.py +21 -0
- node/planes/operations/updates/__init__.py +17 -0
- node/planes/operations/updates/core.py +310 -0
- node/planes/operations/updates/store.py +79 -0
- node/planes/operations/updates/types.py +20 -0
- node/planes/peering/__init__.py +28 -0
- node/planes/peering/budget/__init__.py +43 -0
- node/planes/peering/budget/chain.py +43 -0
- node/planes/peering/budget/core.py +537 -0
- node/planes/peering/budget/evaluation.py +80 -0
- node/planes/peering/budget/meter.py +100 -0
- node/planes/peering/budget/receipts.py +439 -0
- node/planes/peering/budget/shape.py +101 -0
- node/planes/peering/budget/tick.py +110 -0
- node/planes/peering/budget/types.py +67 -0
- node/planes/peering/budget/windows.py +346 -0
- node/planes/peering/capacity/__init__.py +7 -0
- node/planes/peering/capacity/compartment.py +342 -0
- node/planes/peering/capacity/types.py +24 -0
- node/planes/peering/peering/__init__.py +25 -0
- node/planes/peering/peering/core.py +464 -0
- node/planes/peering/peering/envelope.py +142 -0
- node/planes/peering/peering/handshake.py +198 -0
- node/planes/peering/peering/shape.py +80 -0
- node/planes/peering/peering/types.py +39 -0
- node/planes/peering/reporter/__init__.py +42 -0
- node/planes/peering/reporter/constants.py +29 -0
- node/planes/peering/reporter/core.py +299 -0
- node/planes/peering/reporter/finding.py +74 -0
- node/planes/peering/reporter/findings.py +171 -0
- node/planes/peering/reporter/publish.py +285 -0
- node/planes/peering/reporter/reading.py +193 -0
- node/planes/peering/reporter/shape.py +86 -0
- node/planes/peering/reporter/types.py +50 -0
- node/planes/peering/strain/__init__.py +9 -0
- node/planes/peering/strain/compartment.py +204 -0
- node/planes/peering/strain/types.py +21 -0
- node/planes/peering/trust/__init__.py +9 -0
- node/planes/peering/trust/compartment.py +239 -0
- node/planes/peering/trust/types.py +30 -0
- node/planes/services/__init__.py +33 -0
- node/planes/services/compute/__init__.py +160 -0
- node/planes/services/compute/common.py +134 -0
- node/planes/services/compute/compartment.py +681 -0
- node/planes/services/compute/executors/__init__.py +121 -0
- node/planes/services/compute/executors/agent.py +840 -0
- node/planes/services/compute/executors/bacalhau.py +605 -0
- node/planes/services/compute/executors/base.py +127 -0
- node/planes/services/compute/executors/harness.py +439 -0
- node/planes/services/compute/executors/mcp.py +454 -0
- node/planes/services/compute/executors/media.py +898 -0
- node/planes/services/compute/executors/nondet.py +283 -0
- node/planes/services/compute/executors/providers.py +499 -0
- node/planes/services/compute/executors/registry.py +140 -0
- node/planes/services/compute/executors/wasm.py +244 -0
- node/planes/services/compute/executors/zk.py +604 -0
- node/planes/services/compute/jobs.py +227 -0
- node/planes/services/compute/load.py +180 -0
- node/planes/services/compute/market.py +433 -0
- node/planes/services/compute/sandbox.py +282 -0
- node/planes/services/compute/types.py +84 -0
- node/planes/services/compute_wire/__init__.py +58 -0
- node/planes/services/compute_wire/compartment.py +256 -0
- node/planes/services/compute_wire/messages.py +320 -0
- node/planes/services/compute_wire/sealing.py +147 -0
- node/planes/services/compute_wire/session.py +572 -0
- node/planes/services/compute_wire/types.py +49 -0
- node/planes/services/compute_wire/verify.py +331 -0
- node/planes/services/dvm/__init__.py +24 -0
- node/planes/services/dvm/gateway.py +526 -0
- node/planes/services/dvm/protocol.py +35 -0
- node/planes/services/dvm/translate.py +15 -0
- node/planes/services/dvm/types.py +25 -0
- node/planes/services/gateway/__init__.py +40 -0
- node/planes/services/gateway/settle.py +279 -0
- node/planes/services/gateway/translate.py +644 -0
- node/planes/services/gossip/__init__.py +22 -0
- node/planes/services/gossip/core.py +929 -0
- node/planes/services/gossip/envelope.py +154 -0
- node/planes/services/gossip/mesh.py +127 -0
- node/planes/services/gossip/types.py +50 -0
- node/planes/services/nsite/__init__.py +7 -0
- node/planes/services/nsite/client.py +62 -0
- node/planes/services/nsite/compartment.py +752 -0
- node/planes/services/nsite/types.py +44 -0
- node/planes/services/paidfetch/__init__.py +7 -0
- node/planes/services/paidfetch/compartment.py +862 -0
- node/planes/services/paidfetch/types.py +99 -0
- node/planes/services/settle/__init__.py +27 -0
- node/planes/services/settle/compartment.py +384 -0
- node/planes/services/settle/engine.py +456 -0
- node/planes/services/settle/gateways.py +541 -0
- node/planes/services/settle/payment.py +512 -0
- node/planes/services/settle/store.py +74 -0
- node/planes/services/settle/types.py +58 -0
- node/planes/services/sidecars/__init__.py +8 -0
- node/planes/services/sidecars/compartment.py +461 -0
- node/planes/services/sidecars/manifest.py +304 -0
- node/planes/services/sidecars/manifests/10-grasp.json +37 -0
- node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
- node/planes/services/sidecars/manifests/30-magpie.json +28 -0
- node/planes/services/sidecars/types.py +43 -0
- node/planes/services/video/__init__.py +106 -0
- node/planes/services/video/barter.py +407 -0
- node/planes/services/video/cli.py +158 -0
- node/planes/services/video/compartment.py +932 -0
- node/planes/services/video/ingest.py +524 -0
- node/planes/services/video/ledger.py +705 -0
- node/planes/services/video/packager.py +847 -0
- node/planes/services/video/player.py +456 -0
- node/planes/services/video/timed_segments.py +124 -0
- node/planes/services/video/transit.py +478 -0
- node/planes/store/__init__.py +24 -0
- node/planes/store/blob_replication/__init__.py +7 -0
- node/planes/store/blob_replication/compartment.py +496 -0
- node/planes/store/blob_replication/types.py +35 -0
- node/planes/store/blob_wire/__init__.py +48 -0
- node/planes/store/blob_wire/client.py +57 -0
- node/planes/store/blob_wire/core.py +359 -0
- node/planes/store/blob_wire/intake.py +74 -0
- node/planes/store/blob_wire/messages.py +85 -0
- node/planes/store/projected/__init__.py +7 -0
- node/planes/store/projected/compartment.py +258 -0
- node/planes/store/projected/types.py +29 -0
- node/planes/store/retention/__init__.py +7 -0
- node/planes/store/retention/compartment.py +214 -0
- node/planes/store/retention/types.py +12 -0
- node/planes/store/sync/__init__.py +35 -0
- node/planes/store/sync/core.py +311 -0
- node/planes/store/sync/envelope.py +106 -0
- node/planes/store/sync/handover.py +206 -0
- node/planes/store/sync/initiator.py +128 -0
- node/planes/store/sync/responder.py +114 -0
- node/planes/store/sync/rules.py +179 -0
- node/planes/store/sync/shape.py +80 -0
- node/planes/store/sync/types.py +51 -0
- node/planes/transport/__init__.py +23 -0
- node/planes/transport/bridge/__init__.py +13 -0
- node/planes/transport/bridge/compartment.py +785 -0
- node/planes/transport/bridge/ratelimit.py +44 -0
- node/planes/transport/bridge/types.py +43 -0
- node/planes/transport/lan/__init__.py +9 -0
- node/planes/transport/lan/compartment.py +446 -0
- node/planes/transport/lan/types.py +25 -0
- node/planes/transport/link/__init__.py +7 -0
- node/planes/transport/link/compartment.py +257 -0
- node/planes/transport/link/types.py +27 -0
- node/planes/transport/mediation/__init__.py +45 -0
- node/planes/transport/mediation/compartment.py +238 -0
- node/planes/transport/mediation/leaf.py +107 -0
- node/planes/transport/mediation/messages.py +302 -0
- node/planes/transport/mediation/service.py +233 -0
- node/planes/transport/mediation/store.py +37 -0
- node/planes/transport/mediation/types.py +20 -0
- node/planes/transport/p2p/__init__.py +7 -0
- node/planes/transport/p2p/compartment.py +349 -0
- node/planes/transport/p2p/types.py +22 -0
- node/protocols/__init__.py +277 -0
- node/protocols/activitypub/__init__.py +56 -0
- node/protocols/activitypub/actor.py +230 -0
- node/protocols/activitypub/federation.py +490 -0
- node/protocols/activitypub/media.py +145 -0
- node/protocols/activitypub/models.py +286 -0
- node/protocols/activitypub/package.py +271 -0
- node/protocols/activitypub/store.py +564 -0
- node/protocols/arcade/__init__.py +26 -0
- node/protocols/atproto/__init__.py +13 -0
- node/protocols/atproto/firehose.py +126 -0
- node/protocols/atproto/metering.py +165 -0
- node/protocols/atproto/package.py +155 -0
- node/protocols/atproto/repo.py +250 -0
- node/protocols/atproto/xrpc.py +285 -0
- node/protocols/base.py +109 -0
- node/protocols/circles/__init__.py +29 -0
- node/protocols/dm/__init__.py +50 -0
- node/protocols/grasp/__init__.py +22 -0
- node/protocols/isotime.py +15 -0
- node/protocols/nostr/__init__.py +51 -0
- node/protocols/nostr/blobstore.py +383 -0
- node/protocols/nostr/blossom.py +627 -0
- node/protocols/nostr/client.py +377 -0
- node/protocols/nostr/descriptor.py +211 -0
- node/protocols/nostr/eventstore.py +304 -0
- node/protocols/nostr/package.py +182 -0
- node/protocols/nostr/peers.py +109 -0
- node/protocols/nostr/relayserver.py +656 -0
- node/protocols/p2panda/__init__.py +22 -0
- node/protocols/p2panda/identity.py +19 -0
- node/protocols/p2panda/package.py +236 -0
- node/protocols/p2panda/rung.py +152 -0
- node/protocols/p2panda/store.py +273 -0
- node/protocols/rhio/__init__.py +33 -0
- node/protocols/rhio/package.py +275 -0
- node/protocols/rhio/rung.py +136 -0
- node/protocols/rhio/store.py +1423 -0
- node/protocols/ssb/muxrpc.py +256 -0
- node/protocols/ssb/package.py +267 -0
- node/protocols/textile/__init__.py +111 -0
- node/protocols/textile/transducers.py +259 -0
- node/release.py +119 -0
- node/storage/__init__.py +88 -0
- node/storage/accounts.py +305 -0
- node/storage/adapter.py +161 -0
- node/storage/base.py +385 -0
- node/storage/dialect.py +209 -0
- node/storage/grants.py +317 -0
- node/storage/locator.py +150 -0
- node/storage/postgres.py +294 -0
- node/storage/projected.py +953 -0
- node/storage/sharding.py +448 -0
- node/storage/standing.py +568 -0
- node/testnet/__init__.py +9 -0
- node/testnet/cluster.py +731 -0
- node/testnet/fips_daemon.py +230 -0
- node/transport/__init__.py +94 -0
- node/transport/const.py +110 -0
- node/transport/didcomm_inbound.py +251 -0
- node/transport/dnstxt.py +169 -0
- node/transport/fips_rung/__init__.py +67 -0
- node/transport/fips_rung/core.py +666 -0
- node/transport/fips_rung/listener.py +96 -0
- node/transport/fips_rung/types.py +12 -0
- node/transport/host.py +334 -0
- node/transport/iroh_rung/__init__.py +29 -0
- node/transport/iroh_rung/core.py +477 -0
- node/transport/iroh_rung/quic.py +942 -0
- node/transport/iroh_rung/types.py +22 -0
- node/transport/ladder.py +145 -0
- node/transport/lineproto.py +49 -0
- node/transport/mdns.py +303 -0
- node/transport/netfetch.py +283 -0
- node/transport/relay_rung/__init__.py +23 -0
- node/transport/relay_rung/cooldown.py +87 -0
- node/transport/relay_rung/core.py +138 -0
- node/transport/relay_rung/inbound.py +161 -0
- node/transport/relay_rung/ledger.py +104 -0
- node/transport/relay_rung/listen.py +121 -0
- node/transport/relay_rung/outbound.py +181 -0
- node/transport/relay_rung/shape.py +71 -0
- node/transport/relay_rung/types.py +58 -0
- node/transport/relay_rung/wire.py +63 -0
- node/transport/router.py +761 -0
- node/transport/rung.py +276 -0
- node/transport/ssb_rung.py +166 -0
- node/transport/ws.py +502 -0
- sidecar/fleetmesh_announce.py +338 -0
- sidecar/fleetmesh_p2p.py +373 -0
- sidecar/fleetmesh_rhio.py +352 -0
- sidecar/fleetmesh_ssb.py +603 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""The sidecar manifest: one JSON document that says what a fronted service
|
|
2
|
+
is, which capability its presence declares, and the self-tests that have to
|
|
3
|
+
pass before the node says so. `node/sidecars.py` runs them; this module only
|
|
4
|
+
reads and checks the document, so a developer can lint one without a node.
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
"sidecar": 1,
|
|
8
|
+
"name": "grasp",
|
|
9
|
+
"capability": "<origin>/grasp/1.0",
|
|
10
|
+
"endpoint_tag": "grasp-endpoint",
|
|
11
|
+
"checks": [
|
|
12
|
+
{"name": "http", "method": "HEAD", "path": "/"},
|
|
13
|
+
{"name": "git-smart-http", "method": "GET",
|
|
14
|
+
"path": "/fleetmesh-selftest/none.git/info/refs?service=git-upload-pack",
|
|
15
|
+
"expect": [200, 404],
|
|
16
|
+
"content_type": "application/x-git-upload-pack-advertisement"}
|
|
17
|
+
],
|
|
18
|
+
"docs": "https://..."
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Three sources feed the same loader: the manifests shipped in
|
|
22
|
+
`node/sidecar_manifests/` (the built-ins: grasp, ncontainer, magpie), a
|
|
23
|
+
manifest an operator names or inlines in the `sidecars` config block, and
|
|
24
|
+
the ones a plugin class declares. The rules are the same for all three but
|
|
25
|
+
one: only a built-in may declare a capability under the node's own
|
|
26
|
+
origin (the one schema/messages.json's identifiers share), because those identifiers are governed by the schema digest.
|
|
27
|
+
Everyone else uses their own origin, as a dialect does (docs/extending.md).
|
|
28
|
+
|
|
29
|
+
A manifest with no checks is refused. A listener that answers HTTP is not
|
|
30
|
+
evidence that it speaks the protocol, and a capability declared on that
|
|
31
|
+
evidence is a lie a peer acts on.
|
|
32
|
+
"""
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import json
|
|
36
|
+
import os
|
|
37
|
+
import re
|
|
38
|
+
from typing import Any, Mapping, NamedTuple, Optional
|
|
39
|
+
|
|
40
|
+
from fleetmesh_core.identity import origin as origin_mod
|
|
41
|
+
|
|
42
|
+
VERSION = 1
|
|
43
|
+
METHODS = ('HEAD', 'GET')
|
|
44
|
+
# A GET/HEAD that answered with any of these is a live handler for the route:
|
|
45
|
+
# 200 is the happy path, 401/402 a gate in front of it, 404 the route saying
|
|
46
|
+
# "no such object" - which only a handler that owns the route can say. A 5xx,
|
|
47
|
+
# a refused connection or a timeout is not; nor is a 404 from a server that
|
|
48
|
+
# never heard of the path, which is why a check's path is the protocol's own.
|
|
49
|
+
LIVE = frozenset({200, 401, 402, 404})
|
|
50
|
+
_LOCAL_MANIFESTS = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'manifests'))
|
|
51
|
+
BUILTIN_DIR = _LOCAL_MANIFESTS if os.path.isdir(_LOCAL_MANIFESTS) else os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../../sidecar_manifests'))
|
|
52
|
+
|
|
53
|
+
_NAME = re.compile(r'^[a-z][a-z0-9_-]{0,31}$')
|
|
54
|
+
_TAG = re.compile(r'^[a-z][a-z0-9-]{0,63}$')
|
|
55
|
+
_CAPABILITY = re.compile(r'^https://[^/\s]+/[A-Za-z0-9_.-]+/\d+\.\d+$')
|
|
56
|
+
_KEYS = frozenset({'sidecar', 'name', 'capability', 'endpoint_tag', 'checks', 'docs', 'mount', 'pricing', 'run'})
|
|
57
|
+
# How to run it, for whoever deploys it (ops/roles/meshnode reads this;
|
|
58
|
+
# the node never does - it only asks whether the configured endpoint answers
|
|
59
|
+
# the checks). An image published to 127.0.0.1:<port> on the host, the path
|
|
60
|
+
# the container's /data is kept at, env pairs and arguments after the image.
|
|
61
|
+
# Which tag of the image is a host's business (an operator pins a reviewed
|
|
62
|
+
# one in its inventory); the manifest names the image and the default tag.
|
|
63
|
+
_RUN_KEYS = frozenset({'image', 'port', 'data', 'env', 'cmd'})
|
|
64
|
+
_IMAGE = re.compile(r'^[a-z0-9][a-z0-9._/-]*(:[A-Za-z0-9._-]+)?(@sha256:[0-9a-f]{64})?$')
|
|
65
|
+
_ENV_KEY = re.compile(r'^[A-Z][A-Z0-9_]*$')
|
|
66
|
+
_PRICING_KEYS = frozenset({'mode', 'blob_route'})
|
|
67
|
+
# How access is metered, if at all. The proxy in front of a sidecar can only
|
|
68
|
+
# enforce what it can see: one 402 before a whole session (git smart-http is
|
|
69
|
+
# many small requests per clone), or one per content-addressed object whose
|
|
70
|
+
# sha256 is in the URL (then the node's own per-blob route is reused). A
|
|
71
|
+
# protocol whose objects are already fetched through the node's Blossom
|
|
72
|
+
# face (ncontainer) needs nothing here.
|
|
73
|
+
PRICING_MODES = ('none', 'session', 'blob')
|
|
74
|
+
_MOUNT = re.compile(r'^/[A-Za-z0-9_.-]+$')
|
|
75
|
+
_CHECK_KEYS = frozenset({'name', 'method', 'path', 'expect', 'content_type'})
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ManifestError(ValueError):
|
|
79
|
+
"""The document is not a sidecar manifest; the message says why."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
from .types import (
|
|
83
|
+
CheckDict,
|
|
84
|
+
RunDict,
|
|
85
|
+
PricingDict,
|
|
86
|
+
SidecarDict,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Check(NamedTuple):
|
|
91
|
+
"""One self-test: a request the sidecar's protocol must answer."""
|
|
92
|
+
name: str
|
|
93
|
+
method: str
|
|
94
|
+
path: str
|
|
95
|
+
expect: frozenset[int] = LIVE
|
|
96
|
+
content_type: Optional[str] = None # required prefix on a 200, when the protocol fixes one
|
|
97
|
+
|
|
98
|
+
def to_dict(self) -> CheckDict:
|
|
99
|
+
d: CheckDict = {'name': self.name, 'method': self.method, 'path': self.path, 'expect': sorted(self.expect)}
|
|
100
|
+
if self.content_type:
|
|
101
|
+
d['content_type'] = self.content_type
|
|
102
|
+
return d
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Run(NamedTuple):
|
|
106
|
+
"""How a deployer runs the sidecar: a container image published to
|
|
107
|
+
127.0.0.1:<port>, its data at `data` (default /var/lib/meshnode-<name>)."""
|
|
108
|
+
image: str
|
|
109
|
+
port: int
|
|
110
|
+
data: str
|
|
111
|
+
env: tuple[tuple[str, str], ...] = ()
|
|
112
|
+
cmd: str = ''
|
|
113
|
+
|
|
114
|
+
def to_dict(self) -> RunDict:
|
|
115
|
+
d: RunDict = {'image': self.image, 'port': self.port, 'data': self.data}
|
|
116
|
+
if self.env:
|
|
117
|
+
d['env'] = dict(self.env)
|
|
118
|
+
if self.cmd:
|
|
119
|
+
d['cmd'] = self.cmd
|
|
120
|
+
return d
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class Sidecar(NamedTuple):
|
|
124
|
+
"""One fronted service: the name its config block uses, the capability
|
|
125
|
+
its presence declares, the descriptor tag that carries its URL and the
|
|
126
|
+
checks that have to pass before any of that is said."""
|
|
127
|
+
name: str
|
|
128
|
+
capability: str
|
|
129
|
+
tag: str
|
|
130
|
+
checks: tuple[Check, ...]
|
|
131
|
+
docs: str = ''
|
|
132
|
+
builtin: bool = False
|
|
133
|
+
mount: str = '' # public path prefix the proxy fronts it at; '' = '/' + name
|
|
134
|
+
pricing: str = 'none' # one of PRICING_MODES
|
|
135
|
+
blob_route: Optional[str] = None # pricing blob: anchored regex, group 1 = sha256
|
|
136
|
+
run: Optional[Run] = None # how a deployer runs it; the node does not read it
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def mount_path(self) -> str:
|
|
140
|
+
return self.mount or '/' + self.name
|
|
141
|
+
|
|
142
|
+
def to_dict(self) -> SidecarDict:
|
|
143
|
+
d: SidecarDict = {'sidecar': VERSION, 'name': self.name, 'capability': self.capability,
|
|
144
|
+
'endpoint_tag': self.tag, 'checks': [c.to_dict() for c in self.checks]}
|
|
145
|
+
if self.docs:
|
|
146
|
+
d['docs'] = self.docs
|
|
147
|
+
if self.mount:
|
|
148
|
+
d['mount'] = self.mount
|
|
149
|
+
if self.pricing != 'none':
|
|
150
|
+
pr: PricingDict = {'mode': self.pricing}
|
|
151
|
+
if self.blob_route:
|
|
152
|
+
pr['blob_route'] = self.blob_route
|
|
153
|
+
d['pricing'] = pr
|
|
154
|
+
if self.run:
|
|
155
|
+
d['run'] = self.run.to_dict()
|
|
156
|
+
return d
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
origin_of = origin_mod.origin_of # one spelling of the rule; see node/origin.py
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _check(raw: Any, i: int) -> Check:
|
|
163
|
+
where = 'checks[%d]' % i
|
|
164
|
+
if not isinstance(raw, Mapping):
|
|
165
|
+
raise ManifestError('%s must be an object' % where)
|
|
166
|
+
extra = set(raw) - _CHECK_KEYS
|
|
167
|
+
if extra:
|
|
168
|
+
raise ManifestError('%s has unknown keys: %s' % (where, ', '.join(sorted(extra))))
|
|
169
|
+
name = raw.get('name')
|
|
170
|
+
if not isinstance(name, str) or not _TAG.match(name):
|
|
171
|
+
raise ManifestError('%s.name must be a short lowercase identifier' % where)
|
|
172
|
+
method = raw.get('method', 'GET')
|
|
173
|
+
if method not in METHODS:
|
|
174
|
+
raise ManifestError('%s.method must be one of %s' % (where, '/'.join(METHODS)))
|
|
175
|
+
path = raw.get('path')
|
|
176
|
+
if not isinstance(path, str) or not path.startswith('/') or any(ch.isspace() for ch in path):
|
|
177
|
+
raise ManifestError('%s.path must start with / and is appended to the endpoint' % where)
|
|
178
|
+
expect: frozenset[int] = LIVE
|
|
179
|
+
if 'expect' in raw:
|
|
180
|
+
e = raw['expect']
|
|
181
|
+
if (not isinstance(e, list) or not e
|
|
182
|
+
or not all(isinstance(s, int) and not isinstance(s, bool) and 100 <= s <= 599 for s in e)):
|
|
183
|
+
raise ManifestError('%s.expect must be a non-empty list of HTTP statuses' % where)
|
|
184
|
+
if any(s >= 500 for s in e):
|
|
185
|
+
raise ManifestError('%s.expect accepts a 5xx: a server error is never proof of a protocol' % where)
|
|
186
|
+
expect = frozenset(e)
|
|
187
|
+
ctype = raw.get('content_type')
|
|
188
|
+
if ctype is not None and (not isinstance(ctype, str) or '/' not in ctype or any(ch.isspace() for ch in ctype)):
|
|
189
|
+
raise ManifestError('%s.content_type must be a media type such as application/json' % where)
|
|
190
|
+
return Check(name, method, path, expect, ctype)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def parse(raw: Any, *, builtin: bool = False, origin: Optional[str] = None) -> Sidecar:
|
|
194
|
+
"""Read one manifest. `origin` is the reserved origin (the node's own,
|
|
195
|
+
the schema's) that only a built-in may declare under."""
|
|
196
|
+
if not isinstance(raw, Mapping):
|
|
197
|
+
raise ManifestError('a sidecar manifest is a JSON object')
|
|
198
|
+
if raw.get('sidecar') != VERSION:
|
|
199
|
+
raise ManifestError('"sidecar": %d is the manifest version this node reads' % VERSION)
|
|
200
|
+
extra = set(raw) - _KEYS
|
|
201
|
+
if extra:
|
|
202
|
+
raise ManifestError('unknown keys: %s' % ', '.join(sorted(extra)))
|
|
203
|
+
name = raw.get('name')
|
|
204
|
+
if not isinstance(name, str) or not _NAME.match(name):
|
|
205
|
+
raise ManifestError('name must be a short lowercase identifier (it is the config block and the systemd unit)')
|
|
206
|
+
cap = raw.get('capability')
|
|
207
|
+
if not isinstance(cap, str) or not _CAPABILITY.match(cap):
|
|
208
|
+
raise ManifestError('capability must be https://<origin>/<name>/<major>.<minor>')
|
|
209
|
+
if origin and not builtin and origin_mod.reserved(cap, origin):
|
|
210
|
+
raise ManifestError('capability %s is under %s, which only a built-in manifest may declare; '
|
|
211
|
+
'use your own origin' % (cap, origin))
|
|
212
|
+
tag = raw.get('endpoint_tag', name + '-endpoint')
|
|
213
|
+
if not isinstance(tag, str) or not _TAG.match(tag):
|
|
214
|
+
raise ManifestError('endpoint_tag must be a short lowercase identifier')
|
|
215
|
+
checks_raw = raw.get('checks')
|
|
216
|
+
if not isinstance(checks_raw, list) or not checks_raw:
|
|
217
|
+
raise ManifestError('checks must be a non-empty list: a sidecar ships its own self-tests')
|
|
218
|
+
checks = tuple(_check(c, i) for i, c in enumerate(checks_raw))
|
|
219
|
+
seen = [c.name for c in checks]
|
|
220
|
+
if len(set(seen)) != len(seen):
|
|
221
|
+
raise ManifestError('check names repeat: %s' % ', '.join(sorted({n for n in seen if seen.count(n) > 1})))
|
|
222
|
+
docs = raw.get('docs', '')
|
|
223
|
+
if not isinstance(docs, str):
|
|
224
|
+
raise ManifestError('docs must be a URL string')
|
|
225
|
+
mount = raw.get('mount', '')
|
|
226
|
+
if not isinstance(mount, str) or (mount and not _MOUNT.match(mount)):
|
|
227
|
+
raise ManifestError('mount must be a single path segment such as /git (the proxy fronts the sidecar there)')
|
|
228
|
+
pricing, blob_route = _pricing(raw.get('pricing'))
|
|
229
|
+
run = _run(raw.get('run'), name)
|
|
230
|
+
return Sidecar(name, cap, tag, checks, docs, builtin, mount, pricing, blob_route, run)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _run(raw: Any, name: str) -> Optional[Run]:
|
|
234
|
+
if raw is None:
|
|
235
|
+
return None
|
|
236
|
+
if not isinstance(raw, Mapping):
|
|
237
|
+
raise ManifestError('run must be an object {image, port[, data, env, cmd]}')
|
|
238
|
+
extra = set(raw) - _RUN_KEYS
|
|
239
|
+
if extra:
|
|
240
|
+
raise ManifestError('run has unknown keys: %s' % ', '.join(sorted(extra)))
|
|
241
|
+
image = raw.get('image')
|
|
242
|
+
if not isinstance(image, str) or not _IMAGE.match(image):
|
|
243
|
+
raise ManifestError('run.image must be a container image reference such as ghcr.io/org/name:tag')
|
|
244
|
+
port = raw.get('port')
|
|
245
|
+
if not isinstance(port, int) or isinstance(port, bool) or not 1024 <= port <= 65535:
|
|
246
|
+
raise ManifestError('run.port must be an unprivileged TCP port (1024-65535); the host publishes it on loopback')
|
|
247
|
+
data = raw.get('data', '/var/lib/meshnode-' + name)
|
|
248
|
+
if not isinstance(data, str) or not data.startswith('/') or any(ch.isspace() for ch in data):
|
|
249
|
+
raise ManifestError('run.data must be an absolute path; the container sees it as /data')
|
|
250
|
+
env_raw = raw.get('env', {})
|
|
251
|
+
if not isinstance(env_raw, Mapping) or not all(
|
|
252
|
+
isinstance(k, str) and _ENV_KEY.match(k) and isinstance(v, str) for k, v in env_raw.items()):
|
|
253
|
+
raise ManifestError('run.env must map UPPER_CASE names to strings')
|
|
254
|
+
cmd = raw.get('cmd', '')
|
|
255
|
+
if not isinstance(cmd, str):
|
|
256
|
+
raise ManifestError('run.cmd must be a string: the arguments after the image')
|
|
257
|
+
return Run(image, port, data, tuple(sorted(env_raw.items())), cmd)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _pricing(raw: Any) -> tuple[str, Optional[str]]:
|
|
261
|
+
if raw is None:
|
|
262
|
+
return 'none', None
|
|
263
|
+
if not isinstance(raw, Mapping):
|
|
264
|
+
raise ManifestError('pricing must be an object {mode[, blob_route]}')
|
|
265
|
+
extra = set(raw) - _PRICING_KEYS
|
|
266
|
+
if extra:
|
|
267
|
+
raise ManifestError('pricing has unknown keys: %s' % ', '.join(sorted(extra)))
|
|
268
|
+
mode = raw.get('mode', 'none')
|
|
269
|
+
if mode not in PRICING_MODES:
|
|
270
|
+
raise ManifestError('pricing.mode must be one of %s' % '/'.join(PRICING_MODES))
|
|
271
|
+
route = raw.get('blob_route')
|
|
272
|
+
if mode == 'blob':
|
|
273
|
+
if not isinstance(route, str) or not route.startswith('^'):
|
|
274
|
+
raise ManifestError('pricing.blob_route is required for mode blob: an anchored regex whose first group is the sha256')
|
|
275
|
+
try:
|
|
276
|
+
if re.compile(route).groups < 1:
|
|
277
|
+
raise ManifestError('pricing.blob_route needs a capturing group for the sha256')
|
|
278
|
+
except re.error as e:
|
|
279
|
+
raise ManifestError('pricing.blob_route is not a regex: %s' % e) from e
|
|
280
|
+
elif route is not None:
|
|
281
|
+
raise ManifestError('pricing.blob_route only applies to mode blob')
|
|
282
|
+
return mode, route if mode == 'blob' else None
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def load_file(path: str, **kw: Any) -> Sidecar:
|
|
286
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
287
|
+
try:
|
|
288
|
+
raw = json.load(f)
|
|
289
|
+
except ValueError as e:
|
|
290
|
+
raise ManifestError('%s: not JSON (%s)' % (path, e)) from e
|
|
291
|
+
try:
|
|
292
|
+
return parse(raw, **kw)
|
|
293
|
+
except ManifestError as e:
|
|
294
|
+
raise ManifestError('%s: %s' % (path, e)) from e
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def builtins(directory: str = BUILTIN_DIR) -> tuple[Sidecar, ...]:
|
|
298
|
+
"""The manifests shipped with the node, in file-name order - that order
|
|
299
|
+
is the order their capabilities appear on the descriptor."""
|
|
300
|
+
out = []
|
|
301
|
+
for fn in sorted(os.listdir(directory)):
|
|
302
|
+
if fn.endswith('.json') and not fn.startswith('.'):
|
|
303
|
+
out.append(load_file(os.path.join(directory, fn), builtin=True))
|
|
304
|
+
return tuple(out)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sidecar": 1,
|
|
3
|
+
"name": "grasp",
|
|
4
|
+
"capability": "https://fleetmesh.org/grasp/1.0",
|
|
5
|
+
"endpoint_tag": "grasp-endpoint",
|
|
6
|
+
"checks": [
|
|
7
|
+
{
|
|
8
|
+
"name": "http",
|
|
9
|
+
"method": "HEAD",
|
|
10
|
+
"path": "/"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "git-smart-http",
|
|
14
|
+
"method": "GET",
|
|
15
|
+
"path": "/fleetmesh-selftest/none.git/info/refs?service=git-upload-pack",
|
|
16
|
+
"expect": [
|
|
17
|
+
200,
|
|
18
|
+
401,
|
|
19
|
+
402,
|
|
20
|
+
404
|
|
21
|
+
],
|
|
22
|
+
"content_type": "application/x-git-upload-pack-advertisement"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"mount": "/git",
|
|
26
|
+
"pricing": {
|
|
27
|
+
"mode": "session"
|
|
28
|
+
},
|
|
29
|
+
"run": {
|
|
30
|
+
"image": "ghcr.io/nostr-protocol/ngit-grasp:latest",
|
|
31
|
+
"port": 7861,
|
|
32
|
+
"env": {
|
|
33
|
+
"GRASP_LISTEN": "0.0.0.0:7861",
|
|
34
|
+
"GRASP_DATA_DIR": "/data"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sidecar": 1,
|
|
3
|
+
"name": "ncontainer",
|
|
4
|
+
"capability": "https://fleetmesh.org/ncontainer/1.0",
|
|
5
|
+
"endpoint_tag": "ncontainer-endpoint",
|
|
6
|
+
"checks": [
|
|
7
|
+
{
|
|
8
|
+
"name": "http",
|
|
9
|
+
"method": "HEAD",
|
|
10
|
+
"path": "/"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "oci-v2",
|
|
14
|
+
"method": "GET",
|
|
15
|
+
"path": "/v2/",
|
|
16
|
+
"expect": [
|
|
17
|
+
200,
|
|
18
|
+
401
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"mount": "/oci",
|
|
23
|
+
"run": {
|
|
24
|
+
"image": "ghcr.io/ncontainer/ncontainer-gateway:latest",
|
|
25
|
+
"port": 7862,
|
|
26
|
+
"env": {
|
|
27
|
+
"NCONTAINER_LISTEN": "0.0.0.0:7862",
|
|
28
|
+
"NCONTAINER_DATA_DIR": "/data"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sidecar": 1,
|
|
3
|
+
"name": "magpie",
|
|
4
|
+
"capability": "https://fleetmesh.org/magpie/1.0",
|
|
5
|
+
"endpoint_tag": "magpie-endpoint",
|
|
6
|
+
"checks": [
|
|
7
|
+
{
|
|
8
|
+
"name": "http",
|
|
9
|
+
"method": "HEAD",
|
|
10
|
+
"path": "/"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "files-api",
|
|
14
|
+
"method": "HEAD",
|
|
15
|
+
"path": "/api/v1/files/0000000000000000000000000000000000000000000000000000000000000000/content"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"mount": "/magpie",
|
|
19
|
+
"pricing": {
|
|
20
|
+
"mode": "blob",
|
|
21
|
+
"blob_route": "^/api/v1/files/([0-9a-f]{64})/content$"
|
|
22
|
+
},
|
|
23
|
+
"run": {
|
|
24
|
+
"image": "magpie:latest",
|
|
25
|
+
"port": 7863,
|
|
26
|
+
"cmd": "serve --data /data --listen 0.0.0.0:7863 --allow-public"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Types and records for the Sidecars compartment."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any, Mapping, Optional, TypedDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CheckDict(TypedDict, total=False):
|
|
8
|
+
name: str
|
|
9
|
+
method: str
|
|
10
|
+
path: str
|
|
11
|
+
expect: list[int]
|
|
12
|
+
content_type: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RunDict(TypedDict, total=False):
|
|
16
|
+
image: str
|
|
17
|
+
port: int
|
|
18
|
+
data: str
|
|
19
|
+
env: Mapping[str, str]
|
|
20
|
+
cmd: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PricingDict(TypedDict, total=False):
|
|
24
|
+
mode: str
|
|
25
|
+
blob_route: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SidecarDict(TypedDict, total=False):
|
|
29
|
+
sidecar: int
|
|
30
|
+
name: str
|
|
31
|
+
capability: str
|
|
32
|
+
endpoint_tag: str
|
|
33
|
+
checks: list[CheckDict]
|
|
34
|
+
docs: str
|
|
35
|
+
mount: str
|
|
36
|
+
pricing: PricingDict
|
|
37
|
+
run: RunDict
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SidecarStatusReport(TypedDict):
|
|
41
|
+
verified: Optional[bool]
|
|
42
|
+
last_checked: Optional[int]
|
|
43
|
+
checks: list[Mapping[str, Any]]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Fleetmesh Video Delivery & High-Demand Transit Routing subsystem.
|
|
2
|
+
|
|
3
|
+
Composes:
|
|
4
|
+
- `VideoStreamPackager` (Phase 2): CMAF/LL-HLS segment packaging, `RhioBaoTree`
|
|
5
|
+
1024-byte chunked Merkle verification, `p2panda` CBOR manifest logs,
|
|
6
|
+
cross-protocol stream announcements, and `PaidFetch` / `CrossProtocolSettlementGateway`
|
|
7
|
+
HTTP 402 commercial settlement.
|
|
8
|
+
- `TransitReflectorManager` & `MoQFrame` (Phase 3): Automated `Capacity` shed
|
|
9
|
+
handovers (`narrow_scope` / `amend_down` vs. `restore_below`), HTTP 307 viewer
|
|
10
|
+
redirection, MoQ ring-buffer reflection over FIPS port 30820 and Iroh UDP,
|
|
11
|
+
and Rhio NATS JetStream cluster fanout.
|
|
12
|
+
- `TransitLedgerStore` (Phase 4): Record-Then-Answer SQLite persistence for
|
|
13
|
+
bilateral hash-chained `TransitReceipt` IOUs, soft debt ceiling degradation
|
|
14
|
+
(`degraded-audio-only`), and bartered utility repayments (`blob_shoring`,
|
|
15
|
+
`verified_compute`, `dvm`, `mediation`).
|
|
16
|
+
- `VideoStreamCommandProvider` & `register_stream_cli` (Phase 5): CLI commands
|
|
17
|
+
for starting streams, inspecting transit debt, and settling bartered IOUs.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from .barter import (
|
|
22
|
+
BarterHookResult,
|
|
23
|
+
BarterSettlementBridge,
|
|
24
|
+
normalize_peer_pubkey,
|
|
25
|
+
)
|
|
26
|
+
from .cli import (
|
|
27
|
+
VideoStreamCommandProvider,
|
|
28
|
+
cmd_stream,
|
|
29
|
+
register_stream_cli,
|
|
30
|
+
run_stream_cli,
|
|
31
|
+
)
|
|
32
|
+
from .compartment import (
|
|
33
|
+
VideoConfig,
|
|
34
|
+
VideoTransitCompartment,
|
|
35
|
+
)
|
|
36
|
+
from .ingest import (
|
|
37
|
+
CMAFBoxInfo,
|
|
38
|
+
IngestSegmentReport,
|
|
39
|
+
LiveMediaIngestBridge,
|
|
40
|
+
SpoolPollReport,
|
|
41
|
+
make_synthetic_cmaf_init,
|
|
42
|
+
make_synthetic_cmaf_segment,
|
|
43
|
+
parse_cmaf_boxes,
|
|
44
|
+
)
|
|
45
|
+
from .ledger import (
|
|
46
|
+
DEFAULT_UTILITY_BYTES_RATES,
|
|
47
|
+
VALID_UTILITY_KINDS,
|
|
48
|
+
BarterCreditRow,
|
|
49
|
+
DebtSummary,
|
|
50
|
+
OutboundChainState,
|
|
51
|
+
TransitLedgerStore,
|
|
52
|
+
UtilityKind,
|
|
53
|
+
)
|
|
54
|
+
from .packager import (
|
|
55
|
+
SegmentMultiAddress,
|
|
56
|
+
StreamStateRecord,
|
|
57
|
+
VideoStreamPackager,
|
|
58
|
+
)
|
|
59
|
+
from .player import (
|
|
60
|
+
mount_player_routes,
|
|
61
|
+
render_directory_html,
|
|
62
|
+
render_player_html,
|
|
63
|
+
)
|
|
64
|
+
from .transit import (
|
|
65
|
+
MoQFrame,
|
|
66
|
+
MoQFrameDict,
|
|
67
|
+
ReflectionResult,
|
|
68
|
+
TransitReflectorManager,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
'BarterHookResult',
|
|
73
|
+
'BarterSettlementBridge',
|
|
74
|
+
'normalize_peer_pubkey',
|
|
75
|
+
'VideoConfig',
|
|
76
|
+
'VideoTransitCompartment',
|
|
77
|
+
'CMAFBoxInfo',
|
|
78
|
+
'IngestSegmentReport',
|
|
79
|
+
'SpoolPollReport',
|
|
80
|
+
'parse_cmaf_boxes',
|
|
81
|
+
'make_synthetic_cmaf_init',
|
|
82
|
+
'make_synthetic_cmaf_segment',
|
|
83
|
+
'LiveMediaIngestBridge',
|
|
84
|
+
'VideoStreamCommandProvider',
|
|
85
|
+
'cmd_stream',
|
|
86
|
+
'register_stream_cli',
|
|
87
|
+
'run_stream_cli',
|
|
88
|
+
'UtilityKind',
|
|
89
|
+
'VALID_UTILITY_KINDS',
|
|
90
|
+
'DEFAULT_UTILITY_BYTES_RATES',
|
|
91
|
+
'BarterCreditRow',
|
|
92
|
+
'DebtSummary',
|
|
93
|
+
'OutboundChainState',
|
|
94
|
+
'TransitLedgerStore',
|
|
95
|
+
'StreamStateRecord',
|
|
96
|
+
'SegmentMultiAddress',
|
|
97
|
+
'VideoStreamPackager',
|
|
98
|
+
'render_directory_html',
|
|
99
|
+
'render_player_html',
|
|
100
|
+
'mount_player_routes',
|
|
101
|
+
'MoQFrameDict',
|
|
102
|
+
'ReflectionResult',
|
|
103
|
+
'MoQFrame',
|
|
104
|
+
'TransitReflectorManager',
|
|
105
|
+
]
|
|
106
|
+
|