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
node/app/commands.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""NODE_COMMANDS: what the node answers over the control socket, declared where it is.
|
|
2
|
+
|
|
3
|
+
The 26 operator-facing commands answered by NodeApp and the control socket:
|
|
4
|
+
each a Command or an Alias under the node namespace. CONTROL is generated
|
|
5
|
+
from them, and the socket dispatches on them.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import collections
|
|
10
|
+
|
|
11
|
+
from ..compartment import Alias, Command
|
|
12
|
+
from ..config.parameter import Parameter
|
|
13
|
+
from ..interfaces import CommandSpec
|
|
14
|
+
|
|
15
|
+
NODE_COMMANDS: tuple[Command, ...] = (
|
|
16
|
+
Command(
|
|
17
|
+
key='version',
|
|
18
|
+
name='version',
|
|
19
|
+
description='the protocol this socket speaks, the schema digest this node declares, and its identity',
|
|
20
|
+
run='cmd_version',
|
|
21
|
+
reply='{protocol, fleetmesh, digest, did, pubkey, class, commands: [name]}',
|
|
22
|
+
),
|
|
23
|
+
Command(
|
|
24
|
+
key='commands',
|
|
25
|
+
name='commands',
|
|
26
|
+
description='every command this socket answers, with its arguments and reply',
|
|
27
|
+
run='cmd_commands',
|
|
28
|
+
reply='[{name, args: {name: required|optional}, does, reply}]',
|
|
29
|
+
),
|
|
30
|
+
Command(
|
|
31
|
+
key='status',
|
|
32
|
+
name='status',
|
|
33
|
+
description='what the node would write to status.json now',
|
|
34
|
+
run='status',
|
|
35
|
+
reply='the status.json object',
|
|
36
|
+
),
|
|
37
|
+
Command(
|
|
38
|
+
key='health',
|
|
39
|
+
name='health',
|
|
40
|
+
description='every error plane: relays, drops, rejections, failures, throttles',
|
|
41
|
+
run='health',
|
|
42
|
+
reply='the health object status.json carries under health',
|
|
43
|
+
),
|
|
44
|
+
Command(
|
|
45
|
+
key='peers',
|
|
46
|
+
name='peers',
|
|
47
|
+
description='every peer this node holds a grant with',
|
|
48
|
+
run='peer_view',
|
|
49
|
+
reply='[{peer, issued, received, pending, last_evaluated, standing, zeroed_until, pulls, descriptor}]',
|
|
50
|
+
),
|
|
51
|
+
Command(
|
|
52
|
+
key='grant',
|
|
53
|
+
name='grant',
|
|
54
|
+
description='both grants with a peer, their history, and what waits for the boundary',
|
|
55
|
+
parameters=(
|
|
56
|
+
Parameter(key='peer', name='peer', description='peer pubkey', type=str, default='', required=True),
|
|
57
|
+
),
|
|
58
|
+
run='cmd_grant',
|
|
59
|
+
reply='{peer, issued, received, issued_history, received_history, pending, seed_applied}',
|
|
60
|
+
),
|
|
61
|
+
Command(
|
|
62
|
+
key='windows',
|
|
63
|
+
name='windows',
|
|
64
|
+
description='the windows this node priced for a peer, newest last',
|
|
65
|
+
parameters=(
|
|
66
|
+
Parameter(key='peer', name='peer', description='peer pubkey', type=str, default='', required=True),
|
|
67
|
+
Parameter(key='last', name='last', description='limit count', type=int, default=0, required=False),
|
|
68
|
+
),
|
|
69
|
+
run='cmd_windows',
|
|
70
|
+
reply='[{epoch, closed, severity, measured, claimed, receipts_in, my_usage, my_receipts_sent}]',
|
|
71
|
+
),
|
|
72
|
+
Command(
|
|
73
|
+
key='subsystems',
|
|
74
|
+
name='subsystems',
|
|
75
|
+
description='every compartment of this node: what it is, and every refusal it recorded',
|
|
76
|
+
run='cmd_subsystems',
|
|
77
|
+
reply='{name: {..., refusals: {total, by_reason, recent}}}',
|
|
78
|
+
),
|
|
79
|
+
Command(
|
|
80
|
+
key='refusals',
|
|
81
|
+
name='refusals',
|
|
82
|
+
description='every no this node said, across every compartment, newest last',
|
|
83
|
+
parameters=(
|
|
84
|
+
Parameter(key='last', name='last', description='limit count', type=int, default=5, required=False),
|
|
85
|
+
),
|
|
86
|
+
run='cmd_refusals',
|
|
87
|
+
reply='[{subsystem, at, kind, subject, why, detail}]',
|
|
88
|
+
),
|
|
89
|
+
Command(
|
|
90
|
+
key='lifecycle',
|
|
91
|
+
name='lifecycle',
|
|
92
|
+
description='where every compartment is in its lifecycle: running, degraded, '
|
|
93
|
+
'quarantined (breaker open, and until when), failed, retired',
|
|
94
|
+
run='cmd_lifecycle',
|
|
95
|
+
reply='{name: {state, since, failures, trips, until, last_error, started, restarts, '
|
|
96
|
+
'transitions, declared}}',
|
|
97
|
+
),
|
|
98
|
+
Command(
|
|
99
|
+
key='resume',
|
|
100
|
+
name='resume',
|
|
101
|
+
description="end a compartment's quarantine now instead of waiting "
|
|
102
|
+
'out its backoff; a compartment not quarantined is left as it is',
|
|
103
|
+
parameters=(
|
|
104
|
+
Parameter(key='name', name='name', description='compartment name', type=str, default='', required=True),
|
|
105
|
+
),
|
|
106
|
+
run='cmd_resume',
|
|
107
|
+
reply='{name, state}',
|
|
108
|
+
),
|
|
109
|
+
Command(
|
|
110
|
+
key='restart',
|
|
111
|
+
name='restart',
|
|
112
|
+
description='stop and start one compartment in place; only one that '
|
|
113
|
+
'declares Lifecycle(restart=True) - any other is refused',
|
|
114
|
+
parameters=(
|
|
115
|
+
Parameter(key='name', name='name', description='compartment name', type=str, default='', required=True),
|
|
116
|
+
),
|
|
117
|
+
run='cmd_restart',
|
|
118
|
+
reply='{name, state}',
|
|
119
|
+
),
|
|
120
|
+
Command(
|
|
121
|
+
key='events',
|
|
122
|
+
name='events',
|
|
123
|
+
description='the most recent bus events still in memory, oldest first; since is a seq to resume from, topics a list to keep',
|
|
124
|
+
parameters=(
|
|
125
|
+
Parameter(key='since', name='since', description='resume sequence', type=int, default=0, required=False),
|
|
126
|
+
Parameter(key='topics', name='topics', description='topics to filter', type=object, default=None, required=False),
|
|
127
|
+
Parameter(key='last', name='last', description='limit count', type=int, default=0, required=False),
|
|
128
|
+
),
|
|
129
|
+
run='cmd_events',
|
|
130
|
+
reply='{seq, oldest, topics: [name], events: [{seq, ts, topic, data}]}',
|
|
131
|
+
),
|
|
132
|
+
Command(
|
|
133
|
+
key='subscribe',
|
|
134
|
+
name='subscribe',
|
|
135
|
+
description='hold the connection open and stream one {seq, ts, topic, data} line per bus event as it happens; '
|
|
136
|
+
'since replays what the ring still holds first; a slow reader gets meta.dropped with a count',
|
|
137
|
+
parameters=(
|
|
138
|
+
Parameter(key='topics', name='topics', description='topics to filter', type=object, default=None, required=False),
|
|
139
|
+
Parameter(key='since', name='since', description='resume sequence', type=int, default=None, required=False),
|
|
140
|
+
),
|
|
141
|
+
run='cmd_subscribe',
|
|
142
|
+
reply='ack {seq, topics}; then one envelope per line until the reader closes',
|
|
143
|
+
),
|
|
144
|
+
Alias(
|
|
145
|
+
key='strain',
|
|
146
|
+
target='strain.show',
|
|
147
|
+
description="what this node has survived, by direction, and the reserves it sets; never a finding and never on the wire",
|
|
148
|
+
reply='{kinds, directions, weighted_directions, reserve_multiple, dominant_share, flag_tree}',
|
|
149
|
+
),
|
|
150
|
+
Alias(
|
|
151
|
+
key='capacity',
|
|
152
|
+
target='capacity.show',
|
|
153
|
+
description="this node's own ceiling: what it declared, the pressure against it, which shed rungs are engaged and the largest window it has carried",
|
|
154
|
+
reply='{declared, total, pressure, binding, rungs, held, since, measured, peak, suggested, note}',
|
|
155
|
+
),
|
|
156
|
+
Alias(
|
|
157
|
+
key='scheduled',
|
|
158
|
+
target='scheduler.show',
|
|
159
|
+
description='every scheduled pull, when it last ran, and what is holding it back now',
|
|
160
|
+
reply='{pulls, throttled, held, declined_until}',
|
|
161
|
+
),
|
|
162
|
+
Alias(
|
|
163
|
+
key='trust',
|
|
164
|
+
target='trust.show',
|
|
165
|
+
description="the operator's decisions about peers, and which are still in force: "
|
|
166
|
+
"a decision the ladder has since overridden is carried out and not true",
|
|
167
|
+
reply='{decided, in_force, pending, overridden, waiting}',
|
|
168
|
+
),
|
|
169
|
+
Alias(
|
|
170
|
+
key='withdraw',
|
|
171
|
+
target='reporter.withdraw',
|
|
172
|
+
description="withdraw a finding this node made about one window, published or not; the operator's call",
|
|
173
|
+
parameters=(
|
|
174
|
+
Parameter(key='peer', name='peer', description='peer pubkey or did', type=str, default='', required=True),
|
|
175
|
+
Parameter(key='window', name='window', description='window number', type=int, default=0, required=True),
|
|
176
|
+
Parameter(key='why', name='why', description='reason for withdrawal', type=str, default=None, required=False),
|
|
177
|
+
),
|
|
178
|
+
reply='{peer, window, was_published, report, why}',
|
|
179
|
+
),
|
|
180
|
+
Command(
|
|
181
|
+
key='tick',
|
|
182
|
+
name='tick',
|
|
183
|
+
description='one tick now',
|
|
184
|
+
run='cmd_tick',
|
|
185
|
+
reply='[[what, ...], ...] the tick did',
|
|
186
|
+
),
|
|
187
|
+
Command(
|
|
188
|
+
key='pull',
|
|
189
|
+
name='pull',
|
|
190
|
+
description='one sync session with a peer now; refused when the grant does not cover one unless force',
|
|
191
|
+
parameters=(
|
|
192
|
+
Parameter(key='peer', name='peer', description='peer pubkey', type=str, default='', required=True),
|
|
193
|
+
Parameter(key='shard', name='shard', description='shard name', type=str, default='', required=True),
|
|
194
|
+
Parameter(key='filter', name='filter', description='filter object', type=object, default=None, required=True),
|
|
195
|
+
Parameter(key='force', name='force', description='force pull', type=bool, default=False, required=False),
|
|
196
|
+
Parameter(key='timeout', name='timeout', description='timeout in seconds', type=float, default=60.0, required=False),
|
|
197
|
+
),
|
|
198
|
+
run='cmd_pull',
|
|
199
|
+
reply='{status, counters, findings, ...} the session result',
|
|
200
|
+
),
|
|
201
|
+
Command(
|
|
202
|
+
key='terminate',
|
|
203
|
+
name='terminate',
|
|
204
|
+
description='end a peering: zero both grants here and tell the peer',
|
|
205
|
+
parameters=(
|
|
206
|
+
Parameter(key='peer', name='peer', description='peer pubkey', type=str, default='', required=True),
|
|
207
|
+
Parameter(key='reason', name='reason', description='reason for termination', type=str, default='operator', required=False),
|
|
208
|
+
),
|
|
209
|
+
run='cmd_terminate',
|
|
210
|
+
reply='{peer, reason, told: {relay: [accepted, note]}, pulls_removed}',
|
|
211
|
+
),
|
|
212
|
+
Command(
|
|
213
|
+
key='standing',
|
|
214
|
+
name='standing',
|
|
215
|
+
description='ask a peer what it makes of a third node, over standing/1.0',
|
|
216
|
+
parameters=(
|
|
217
|
+
Parameter(key='peer', name='peer', description='peer pubkey', type=str, default='', required=True),
|
|
218
|
+
Parameter(key='target', name='target', description='target pubkey', type=str, default='', required=True),
|
|
219
|
+
),
|
|
220
|
+
run='cmd_standing',
|
|
221
|
+
reply='{answer: tell|decline, body}',
|
|
222
|
+
),
|
|
223
|
+
Command(
|
|
224
|
+
key='reports',
|
|
225
|
+
name='reports',
|
|
226
|
+
description="the kind 30802s about a node on this node's relays, weighed by this node",
|
|
227
|
+
parameters=(
|
|
228
|
+
Parameter(key='target', name='target', description='target pubkey', type=str, default='', required=True),
|
|
229
|
+
),
|
|
230
|
+
run='cmd_reports',
|
|
231
|
+
reply='{view, reports: [{reporter, severity, admissible, reasons, weight, id, created_at}]}',
|
|
232
|
+
),
|
|
233
|
+
Command(
|
|
234
|
+
key='republish',
|
|
235
|
+
name='republish',
|
|
236
|
+
description='send the descriptor to the relays again',
|
|
237
|
+
run='cmd_republish',
|
|
238
|
+
reply='{descriptor, relays: {url: [accepted, note]}}',
|
|
239
|
+
),
|
|
240
|
+
Command(
|
|
241
|
+
key='retire',
|
|
242
|
+
name='retire',
|
|
243
|
+
description='leave the mesh in order: close every asserted range, hand the shards to every '
|
|
244
|
+
'granted peer, withdraw the claims, and stop. Not a restart',
|
|
245
|
+
parameters=(
|
|
246
|
+
Parameter(key='reason', name='reason', description='reason for retirement', type=str, default='operator', required=False),
|
|
247
|
+
Parameter(key='revoke', name='revoke', description='revoke identity event', type=bool, default=False, required=False),
|
|
248
|
+
Parameter(key='lapses_in', name='lapses_in', description='seconds until claim lapses', type=int, default=86400, required=False),
|
|
249
|
+
),
|
|
250
|
+
run='cmd_retire',
|
|
251
|
+
reply='{reason, at, did, peers, handed, withdrawn, revoked}',
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
NODE_COMMANDS_BY_KEY: dict[str, Command] = {c.key: c for c in NODE_COMMANDS}
|
|
256
|
+
|
|
257
|
+
CONTROL: collections.OrderedDict[str, CommandSpec] = collections.OrderedDict([
|
|
258
|
+
(c.key, c.to_spec()) for c in NODE_COMMANDS
|
|
259
|
+
])
|
|
260
|
+
|
|
261
|
+
__all__ = [
|
|
262
|
+
'CONTROL',
|
|
263
|
+
'NODE_COMMANDS',
|
|
264
|
+
'NODE_COMMANDS_BY_KEY',
|
|
265
|
+
]
|
node/app/config.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""The node's configuration: the control-socket table, the defaults, and the
|
|
2
|
+
file that carries them.
|
|
3
|
+
|
|
4
|
+
Separate from the node so `meshnode init`, `peer` and `trust` can read and
|
|
5
|
+
write config.json without constructing a NodeApp.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any, Mapping, Sequence
|
|
12
|
+
|
|
13
|
+
from fleetmesh_core.identity.did import node_of_did
|
|
14
|
+
from ..config import ConfigError, NodeConfig, Problems
|
|
15
|
+
from .commands import CONTROL, NODE_COMMANDS, NODE_COMMANDS_BY_KEY
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'CONTROL',
|
|
19
|
+
'ConfigError',
|
|
20
|
+
'DEFAULTS',
|
|
21
|
+
'NODE_COMMANDS',
|
|
22
|
+
'NODE_COMMANDS_BY_KEY',
|
|
23
|
+
'NodeConfig',
|
|
24
|
+
'Problems',
|
|
25
|
+
'config_from_manifests',
|
|
26
|
+
'defaults',
|
|
27
|
+
'load_config',
|
|
28
|
+
'save_config',
|
|
29
|
+
'typed_config',
|
|
30
|
+
'validate_config',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# The file's defaults are the file shape of NodeConfig(): one source. The
|
|
34
|
+
# comments that used to sit beside each key are on the dataclass fields in
|
|
35
|
+
# node/config/. Only the node's own keys are written; a compartment's block
|
|
36
|
+
# is absent until the operator asks for it, because presence has meaning.
|
|
37
|
+
DEFAULTS: Mapping[str, Any] = NodeConfig.defaults_dict()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def defaults() -> Mapping[str, Any]:
|
|
41
|
+
"""A fresh copy, nested values included.
|
|
42
|
+
|
|
43
|
+
DEFAULTS holds dicts and lists, and dict(DEFAULTS) copies only the top
|
|
44
|
+
level: two nodes built from it shared one `face` dict, so the first to
|
|
45
|
+
record its port set it for every node after. Anything that hands out
|
|
46
|
+
defaults hands out copies.
|
|
47
|
+
"""
|
|
48
|
+
return json.loads(json.dumps(DEFAULTS))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def validate_config(cfg: Any) -> list[str]:
|
|
52
|
+
"""Every problem with a config, as a list of sentences; empty if none.
|
|
53
|
+
|
|
54
|
+
The file is the interface, so it is checked the way a message is: at the
|
|
55
|
+
door, with reasons. NodeConfig.check does the checking; this is the list
|
|
56
|
+
the CLI and the tests read.
|
|
57
|
+
"""
|
|
58
|
+
p = Problems()
|
|
59
|
+
NodeConfig.check(cfg, p)
|
|
60
|
+
return list(p)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def typed_config(cfg: Mapping[str, Any]) -> NodeConfig:
|
|
64
|
+
"""The typed view of a config that validate_config passed."""
|
|
65
|
+
return NodeConfig.from_dict(cfg)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def load_config(directory: str) -> Mapping[str, Any]:
|
|
69
|
+
with open(os.path.join(directory, 'config.json'), encoding='utf-8') as f:
|
|
70
|
+
cfg = json.load(f)
|
|
71
|
+
out = dict(defaults())
|
|
72
|
+
if not isinstance(cfg, dict):
|
|
73
|
+
raise ConfigError('config.json is not an object')
|
|
74
|
+
out.update(cfg)
|
|
75
|
+
problems = validate_config(out)
|
|
76
|
+
if problems:
|
|
77
|
+
raise ConfigError('config.json: ' + '; '.join(problems))
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _blob_storage_cap(cfg: Mapping[str, Any]) -> int | None:
|
|
82
|
+
"""Byte cap for the Blossom blob store, or None for unbounded.
|
|
83
|
+
|
|
84
|
+
FLEETMESH_BLOB_MAX_BYTES wins, then config ``blobs.max_storage_bytes``, then
|
|
85
|
+
the MeshNode manifest's ``storage.maxBytes`` (config_from_manifests copies
|
|
86
|
+
it to ``cfg['storage']``). Before this nothing passed a cap, so the LRU
|
|
87
|
+
eviction in BlobStore.put never ran and a node's disk filled without bound.
|
|
88
|
+
0 or a negative value means unbounded.
|
|
89
|
+
"""
|
|
90
|
+
raw: Any = os.environ.get('FLEETMESH_BLOB_MAX_BYTES')
|
|
91
|
+
if raw in (None, ''):
|
|
92
|
+
raw = (cfg.get('blobs') or {}).get('max_storage_bytes')
|
|
93
|
+
if raw in (None, ''):
|
|
94
|
+
raw = (cfg.get('storage') or {}).get('maxBytes')
|
|
95
|
+
try:
|
|
96
|
+
cap = int(raw) if raw not in (None, '') else 0
|
|
97
|
+
except (TypeError, ValueError):
|
|
98
|
+
raise ConfigError(f'blob storage cap is not an integer: {raw!r}')
|
|
99
|
+
return cap if cap > 0 else None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def save_config(directory: str, cfg: Mapping[str, Any]) -> None:
|
|
103
|
+
os.makedirs(directory, mode=0o700, exist_ok=True)
|
|
104
|
+
path = os.path.join(directory, 'config.json')
|
|
105
|
+
tmp = path + '.tmp'
|
|
106
|
+
with open(tmp, 'w', encoding='utf-8') as f:
|
|
107
|
+
json.dump(cfg, f, indent=2, sort_keys=True)
|
|
108
|
+
f.write('\n')
|
|
109
|
+
os.replace(tmp, path)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def config_from_manifests(meshnode: Mapping[str, Any], peerings: Sequence[Mapping[str, Any]] = ()) -> Mapping[str, Any]:
|
|
113
|
+
"""A config from a MeshNode manifest and any Peering manifests (parsed YAML or JSON)."""
|
|
114
|
+
spec = meshnode['spec']
|
|
115
|
+
cfg = dict(defaults())
|
|
116
|
+
cfg['class'] = spec['class']
|
|
117
|
+
cfg['operator'] = spec['operator']['pubkey']
|
|
118
|
+
cfg['endpoints'] = [{'transport': e['transport'], 'address': e.get('address') or e.get('ticket'),
|
|
119
|
+
'priority': e.get('priority', 50)} for e in spec.get('endpoints', [])]
|
|
120
|
+
caps = spec.get('capabilities', [])
|
|
121
|
+
cfg['capabilities'] = [c for c in caps if not c.startswith('https://')]
|
|
122
|
+
cfg['protocols'] = sorted(set(DEFAULTS['protocols']) | {c for c in caps if c.startswith('https://')})
|
|
123
|
+
cfg['storage'] = spec.get('storage', {})
|
|
124
|
+
cfg['peers'] = []
|
|
125
|
+
for pm in peerings:
|
|
126
|
+
ps = pm['spec']
|
|
127
|
+
cfg['peers'].append({'pubkey': node_of_did(ps['peer']), 'shard': pm['metadata']['name'],
|
|
128
|
+
'filter': ps['ingress']['filter'], 'pull_every': 600,
|
|
129
|
+
'window': ps.get('budget', {}).get('window')})
|
|
130
|
+
return cfg
|
|
131
|
+
|