tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

commit 7766cefd12ce18e5f3b9e47bb283d47d414d6fdc
parent 00f506fe318e69a0e4933513c061e3d49215ac4b
Author: triesap <tyson@radroots.org>
Date:   Sun, 14 Jun 2026 14:39:34 -0700

config: move relay example config

- Move the checked-in relay example config out of ops/production.
- Update runtime config and logging fixtures to use config/tangle.example.json.
- Align CLI tests with the neutral example config path.
- Remove the misleading production ops directory from the repo tree.

Diffstat:
Aconfig/tangle.example.json | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/tangle/src/lib.rs | 4++--
Mcrates/tangle_runtime/src/config.rs | 14+++++++-------
Mcrates/tangle_runtime/src/logging.rs | 4++--
4 files changed, 182 insertions(+), 11 deletions(-)

diff --git a/config/tangle.example.json b/config/tangle.example.json @@ -0,0 +1,171 @@ +{ + "server": { + "listen_addr": "0.0.0.0:7000", + "relay_url": "wss://relay.radroots.test" + }, + "pocket": { + "data_directory": "runtime/pocket", + "map_size_bytes": 1099511627776, + "reader_slots": 512, + "sync_policy": "flush_on_shutdown" + }, + "groups": { + "enabled": true, + "canonical_relay_url": "wss://relay.radroots.test", + "relay_secret": "7777777777777777777777777777777777777777777777777777777777777777", + "owner_pubkeys": [ + "0000000000000000000000000000000000000000000000000000000000000001" + ], + "admin_pubkeys": [ + "0000000000000000000000000000000000000000000000000000000000000002" + ], + "policy": { + "public_join": false, + "invites_enabled": false + }, + "limits": { + "max_group_id_bytes": 128, + "max_group_tags_per_event": 8, + "max_supported_kinds": 512, + "max_member_list_pubkeys": 100000, + "max_outbox_replay_batch": 1000 + } + }, + "auth": { + "challenge_ttl_seconds": 300, + "created_at_skew_seconds": 600 + }, + "limits": { + "max_message_length": 1048576, + "max_subid_length": 64, + "max_subscriptions_per_connection": 64, + "max_filters_per_request": 10, + "max_tag_values_per_filter": 100, + "max_query_complexity": 2048, + "max_limit": 500, + "default_limit": 100, + "max_event_tags": 200, + "max_content_length": 65536, + "broadcast_channel_capacity": 4096, + "per_connection_outbound_queue": 256 + }, + "rate_limits": { + "auth": { + "per_ip": { + "window_seconds": 60, + "max_hits": 120 + }, + "per_pubkey": { + "window_seconds": 60, + "max_hits": 30 + }, + "failures": { + "window_seconds": 300, + "max_hits": 5 + }, + "failures_per_ip": { + "window_seconds": 300, + "max_hits": 20 + } + }, + "event": { + "per_ip": { + "window_seconds": 60, + "max_hits": 600 + }, + "per_pubkey": { + "window_seconds": 60, + "max_hits": 120 + }, + "per_kind": { + "window_seconds": 60, + "max_hits": 1000 + } + }, + "group": { + "write_per_ip": { + "window_seconds": 60, + "max_hits": 300 + }, + "write_per_pubkey": { + "window_seconds": 60, + "max_hits": 60 + }, + "write_per_group": { + "window_seconds": 60, + "max_hits": 90 + }, + "write_per_kind": { + "window_seconds": 60, + "max_hits": 300 + }, + "join_flow": { + "window_seconds": 300, + "max_hits": 10 + }, + "join_flow_per_ip": { + "window_seconds": 300, + "max_hits": 30 + } + }, + "req": { + "per_ip": { + "window_seconds": 60, + "max_hits": 600 + }, + "per_connection": { + "window_seconds": 60, + "max_hits": 120 + }, + "per_pubkey": { + "window_seconds": 60, + "max_hits": 240 + }, + "per_group": { + "window_seconds": 60, + "max_hits": 240 + }, + "per_kind": { + "window_seconds": 60, + "max_hits": 500 + }, + "broad": { + "window_seconds": 60, + "max_hits": 30 + } + }, + "count": { + "per_ip": { + "window_seconds": 60, + "max_hits": 300 + }, + "per_connection": { + "window_seconds": 60, + "max_hits": 60 + }, + "per_pubkey": { + "window_seconds": 60, + "max_hits": 120 + }, + "per_group": { + "window_seconds": 60, + "max_hits": 120 + }, + "per_kind": { + "window_seconds": 60, + "max_hits": 240 + }, + "broad": { + "window_seconds": 60, + "max_hits": 20 + } + } + }, + "observability": { + "tracing": { + "enabled": true, + "filter": "info,tangle=info,tangle_runtime=info,tangle_groups=info,tangle_store_pocket=info", + "format": "json" + } + } +} diff --git a/crates/tangle/src/lib.rs b/crates/tangle/src/lib.rs @@ -193,11 +193,11 @@ mod tests { TangleInvocation::new(TangleCommand::Version, None) ); assert_eq!( - parse_tangle_invocation(["run", "--config", "ops/production/tangle-v2.example.json"]) + parse_tangle_invocation(["run", "--config", "config/tangle.example.json"]) .expect("run"), TangleInvocation::new( TangleCommand::Run, - Some("ops/production/tangle-v2.example.json".to_owned()) + Some("config/tangle.example.json".to_owned()) ) ); } diff --git a/crates/tangle_runtime/src/config.rs b/crates/tangle_runtime/src/config.rs @@ -616,7 +616,7 @@ mod tests { #[test] fn base_relay_runtime_config_parses_v2_production_example() { let config = parse_base_relay_runtime_config_json(include_str!( - "../../../ops/production/tangle-v2.example.json" + "../../../config/tangle.example.json" )) .expect("config"); @@ -933,7 +933,7 @@ mod tests { #[test] fn base_relay_runtime_config_requires_explicit_query_complexity() { - let raw = include_str!("../../../ops/production/tangle-v2.example.json") + let raw = include_str!("../../../config/tangle.example.json") .replace(" \"max_query_complexity\": 2048,\n", ""); assert!( parse_base_relay_runtime_config_json(&raw) @@ -945,7 +945,7 @@ mod tests { #[test] fn base_relay_runtime_config_requires_ip_scoped_rate_limits() { - let raw = include_str!("../../../ops/production/tangle-v2.example.json").replace( + let raw = include_str!("../../../config/tangle.example.json").replace( " \"per_ip\": {\n \"window_seconds\": 60,\n \"max_hits\": 120\n },\n", "", ); @@ -956,7 +956,7 @@ mod tests { .contains("missing field `per_ip`") ); - let raw = include_str!("../../../ops/production/tangle-v2.example.json").replace( + let raw = include_str!("../../../config/tangle.example.json").replace( " \"failures\": {\n \"window_seconds\": 300,\n \"max_hits\": 5\n },\n \"failures_per_ip\": {\n \"window_seconds\": 300,\n \"max_hits\": 20\n }\n", " \"failures\": {\n \"window_seconds\": 300,\n \"max_hits\": 5\n }\n", ); @@ -967,7 +967,7 @@ mod tests { .contains("missing field `failures_per_ip`") ); - let raw = include_str!("../../../ops/production/tangle-v2.example.json").replace( + let raw = include_str!("../../../config/tangle.example.json").replace( " \"per_ip\": {\n \"window_seconds\": 60,\n \"max_hits\": 600\n },\n", "", ); @@ -978,7 +978,7 @@ mod tests { .contains("missing field `per_ip`") ); - let raw = include_str!("../../../ops/production/tangle-v2.example.json").replace( + let raw = include_str!("../../../config/tangle.example.json").replace( " \"write_per_ip\": {\n \"window_seconds\": 60,\n \"max_hits\": 300\n },\n", "", ); @@ -989,7 +989,7 @@ mod tests { .contains("missing field `write_per_ip`") ); - let raw = include_str!("../../../ops/production/tangle-v2.example.json").replace( + let raw = include_str!("../../../config/tangle.example.json").replace( " \"join_flow\": {\n \"window_seconds\": 300,\n \"max_hits\": 10\n },\n \"join_flow_per_ip\": {\n \"window_seconds\": 300,\n \"max_hits\": 30\n }\n", " \"join_flow\": {\n \"window_seconds\": 300,\n \"max_hits\": 10\n }\n", ); diff --git a/crates/tangle_runtime/src/logging.rs b/crates/tangle_runtime/src/logging.rs @@ -419,7 +419,7 @@ mod tests { #[test] fn runtime_log_summary_never_contains_relay_secret() { - let raw = include_str!("../../../ops/production/tangle-v2.example.json"); + let raw = include_str!("../../../config/tangle.example.json"); let config = parse_base_relay_runtime_config_json(raw).expect("config"); let secret = "7".repeat(64); let summary = TangleRuntimeLogSummary::from_config(&config); @@ -434,7 +434,7 @@ mod tests { #[test] fn structured_runtime_config_log_redacts_relay_secret() { - let raw = include_str!("../../../ops/production/tangle-v2.example.json"); + let raw = include_str!("../../../config/tangle.example.json"); let config = parse_base_relay_runtime_config_json(raw).expect("config"); let secret = "7".repeat(64); let writer = CapturedWriter::default();