Skip to content

feat: add Dedicated and Direct network presets and configurable mesh policy - #71

Merged
lemon-mint merged 4 commits into
mainfrom
feat/private-network-presets
Oct 20, 2026
Merged

lemon-mint merged 4 commits into
mainfrom
feat/private-network-presets

Conversation

@lemon-mint

@lemon-mint lemon-mint commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Overview

IVNP already lets a single Router and Destination span multiple overlay networks by netId (1..255). Until now, however, every network context inherited parameters calibrated for the global public I2P network (netId = 2), which assumes tens of thousands of untrusted routers spread across thousands of autonomous systems.

When an engineer embeds IVNP into a 2-device desktop clipboard-sync sidecar on home Wi-Fi (192.168.1.0/24), a 10-node microservice cluster on an office VLAN (10.10.0.0/16), or a multi-region dedicated overlay, those public defaults stall the mesh before a single stream can open:

  • K-Bucket /16 Subnet Quotas (BucketSubnetQuota = 2) silently drop the 3rd node sharing a LAN or cloud VPC /16 prefix.
  • Tunnel Hop /16 Diversity (IPRestriction = 2) refuses to place two hops in the same /16 subnet, making tunnel builds impossible on a flat subnet.
  • 2-Node 1-Hop Self-Relay (OBEP == IBGW) rejects outbound tunnel builds when the only remote peer in a 2-node mesh is also the gateway for the inbound reply tunnel.
  • Unreachable Quorums (MinRouters = 50, FloodfillK = 4..5) prevent WaitReady and LeaseSet2 publication confirmation from ever completing in small clusters.
  • Public Bootstrap Defaults poll clearnet netId = 2 reseed servers, .i2p addressbook feeds, and public HTTP outproxies in air-gapped or private environments.

This PR makes all 16 underlying NetDB, tunnel, and reseed parameters configurable via both ivnp.conf and the root Go API (gosuda.org/ivnp), resolves the 2-node 1-hop OBEP == IBGW build and publication edge cases in controlplane, and introduces two first-class presets alongside public: dedicated (NetworkProfileDedicated) and direct (NetworkProfileDirect).


Network & Destination Presets

Preset Profile Go Constructors (gosuda.org/ivnp) Target Topology & Use Cases Key Parameter Adjustments
public (NetworkProfilePublic) DefaultI2PNetwork() / NewNetwork(...)
DefaultDestinationConfig()
Global public I2P network (netId = 2) and large untrusted overlays (200+ nodes) Preserves 100% of existing public I2P defaults (MinRouters = 50, /16 bucket quota 2, IPRestriction = 2, 2-hop exploratory / 3-hop client tunnels, K = 4..5, 60s probes)
dedicated (NetworkProfileDedicated) NewDedicatedNetwork(name, netID, opts...)
DedicatedDestinationConfig()
Multi-site or multi-region dedicated overlays (3 to 200+ nodes) requiring multi-floodfill redundancy and hop diversity Enables floodfill + transit (ParticipationContributor), relaxes /16 bucket quota to 16, sets MinRouters = 2 and FloodfillK = 2, builds 1-hop tunnels (2 active per direction), sets 5s maintenance / 10s probes, and disables public reseed/outproxies
direct (NetworkProfileDirect) NewDirectNetwork(name, netID, opts...)
DirectDestinationConfig()
Personal device sidecars (clipboard/file sync), office LANs, Docker/VPC bridges, and 2-node direct meshes (2 to 30 nodes) Enables floodfill + transit, disables /16 bucket quotas (0) and hop IP restrictions (0), sets MinRouters = 1 and FloodfillK = 1 (works with 2 nodes total), builds 1-hop tunnels (1 active + 1 backup), and sets fast 3s maintenance / 5s probes

Example: 2-Device Clipboard Sync Sidecar (direct + public Happy Eyeballs)

cfg := ivnp.DefaultRouterConfig()
cfg.SetNetworks(
	ivnp.NewDirectNetwork("clipsync", 88,
		ivnp.WithListenAddress(bindAddr, bindAddr),
		ivnp.WithAdvertisedAddress(lanAddr, lanAddr),
		ivnp.WithPeerSeeds(pairedDeviceRouterInfos...),
		ivnp.WithPeerAdmission(ivnp.AdmitPeers(pairedDeviceHashes...)),
	),
	ivnp.DefaultI2PNetwork(),
)
cfg.DefaultNetwork = "clipsync"

router, err := ivnp.NewRouter(ctx, cfg)
if err != nil {
	return err
}
defer router.Close()

destCfg := ivnp.DirectDestinationConfig()
destCfg.Networks = []string{"clipsync", "i2p"}
dest, err := router.NewDestination(ctx, destCfg)

Key Changes Across Layers

1. L3 state: Profile Baseline & Raw INI Configuration (state/internal/configuration)

  • Added NetworkProfile ("public", "dedicated", "direct") and DefaultOperatingForProfile(profile) in operating.go.
  • Exposed [network] profile in ivnp.conf; profile defaults are applied first, and any explicit key in [router], [netdb], [tunnel], [reseed], [http_proxy], or [addressbook] overrides the preset.
  • Added INI parsing and bounds validation for:
    • [netdb]: bucket_subnet_quota (0 disables /16 quota), min_routers, router_info_floodfill_k, lease_set_floodfill_k, exploration_bootstrap_delay, exploration_steady_delay, and raised maxBootstrapRouterInfoFiles from 8 to 64.
    • [tunnel]: ip_restriction (0..4, where 0 disables subnet restriction and 1..4 enforce /8, /16, /24, /32), probe_timeout, and probe_failure_threshold.
    • [reseed]: bucket_subnet_quota (0..64), bucket_admit_limit, verify_anchor_count, merge_wait, trusted_cert_files (up to 16 PEM certificates), and dynamic ?netid=<network.id> URL query verification instead of hardcoded ?netid=2.

2. L5 controlplane: Small-Mesh Convergence, 2-Node Self-Relay & Custom SU3 Signers

  • 2-Node OBEP == IBGW Coalesced Reply Handling (controlplane/internal/tunnel):
    • When a 2-node mesh has only 1 remote peer, selectDiverseHops falls back to allowing the outbound endpoint to match the reply tunnel's inbound gateway (AllowCoalescedReply = true), and BuildManager.inboundLocalDelivery routes I2NPOutboundTunnelBuildReply frames arriving at the local inbound tunnel endpoint directly to HandleReply.
    • Added small-mesh candidate fallbacks (len(refs) <= 8 and relaxSubTier) that remain completely inert on the public network (50+ routers).
  • Deduplicated Floodfill Confirmations (controlplane/internal/netdb/confirmation.go):
    • confirmedPublication now tracks confirmedTargets per generation so retry ticks do not re-send duplicate DatabaseStore messages to floodfills that already acknowledged the current generation.
  • Custom Reseed Certificates & Subnet Quotas (controlplane/internal/reseed):
    • Added ParseSU3SignerCertificate and MergeSU3Signers to validate and merge custom RSA-4096 X.509 certificates from trusted_cert_files or WithTrustedReseedCerts.
    • Added DisableBucketSubnetQuota to reseed.Client so single-subnet private reseeds are not throttled by /16 ingress quotas.
  • Configurable Readiness (controlplane/internal/runtime):
    • Wired MinNetDBRouters, RouterInfoFloodfillK, LeaseSetFloodfillK, ExplorationBootstrapDelay, ExplorationSteadyDelay, and tunnelIPRestriction through Controller and destinationRuntimeFactory.

3. L8 Root Go API & Documentation (gosuda.org/ivnp, docs/)

  • Added NewDedicatedNetwork, NewDirectNetwork, DedicatedDestinationConfig, DirectDestinationConfig, NetDBConfig, TunnelPolicyConfig, WithNetworkProfile, WithListenAddress, WithAdvertisedAddress, WithPeerSeeds, WithTrustedReseedCerts, WithNetDBConfig, WithTunnelTiming, WithIPRestriction, and WithPeerAdmission.
  • Added a dedicated technical guide in docs/private-networks-and-presets.md covering failure modes of public defaults on LANs, realistic deployment scenarios, the complete raw parameter matrix, and runnable Go and ivnp.conf recipes.

Public I2P Network Compatibility

  • Zero changes to L0-L4 wire protocols or data plane: cryptography, foundation, and dataplane (NTCP2, SSU2, 1024-byte tunnel crypto, ECIES ratchet, streaming, and datagrams) are untouched.
  • Identical public defaults: DefaultOperating(), DefaultRouterConfig(), DefaultI2PNetwork(), and DefaultDestinationConfig() produce the exact same parameter values as main.
  • Guarded fallbacks: Small-mesh hop-selection fallbacks in controlplane/internal/tunnel/netdb_source.go trigger only when standard candidate selection cannot fill wanted hops in sparse tables (len(refs) <= 8).

Verification

  • Static Analysis & Architecture:
    • go run gosuda.org/ivnp/tools/importformatter -write
    • gojgp lint ./... (0 issues)
    • go test -v ./architecture (L0-L9 acyclic DAG and canonical import checks pass)
  • Unit & Subsystem Tests:
    • go test ./state/... ./controlplane/... ./client/... ./node/... ./cmd/...
    • TestParseOperatingNetworkProfilesAndOverrides, TestConfirmedPublicationDoesNotResendToAlreadyConfirmedTargets, TestMergeSU3SignersAdmitsCustomPEMCertificate, TestSmallNetworkHopSelectionFallbacks, TestNetworkPresetsAndOptions
  • Embedded & Live Loopback E2E Tests (multi_network_test.go):
    • TestEmbeddedTwoNodeDirectMesh: 2-node direct mesh on a shared /24 subnet with 1-hop tunnels and stream round-trip.
    • TestEmbeddedDedicatedMeshWithPeerAdmission: 3-node dedicated mesh on a shared /24 subnet with AdmitPeers allowlist, stream, and Datagram2 round-trip.
    • TestE2ELoopbackDirectMesh: 2 embedded routers over live kernel TCP/UDP loopback sockets (ExportLocalRouterInfo / ImportRouterInfo, WaitReady, and end-to-end streaming).
  • Deterministic Simulation Testing (sim_dst_test.go):
    • TestSimDirectTwoNodeMesh (DST_SEED=2088) and TestSimDedicatedMesh (DST_SEED=2042) pass under testing/synctest with dst_overlay.json.

…le mesh policy

- Expose NetDB subnet quotas, readiness router thresholds, floodfill K targets, exploration cadence, tunnel IP restrictions, and custom SU3 signer certificates across state, controlplane, and root SDK
- Add small_private and office_lan presets (NewSmallPrivateNetwork, NewOfficeLANNetwork, SmallPrivateDestinationConfig, OfficeLANDestinationConfig)
- Deduplicate confirmed floodfill targets across publication retry waves and add 2-node same-gateway (OBEP == IBGW) tunnel build fallbacks
- Add E2E and deterministic simulation tests for 2-node OfficeLAN and 4-node SmallPrivate meshes
@lemon-mint lemon-mint changed the title Feat/private network presets feat: add Dedicated and Direct network presets and configurable mesh policy Oct 20, 2026
@lemon-mint
lemon-mint merged commit 0a1b746 into main Oct 20, 2026
20 checks passed
@lemon-mint
lemon-mint deleted the feat/private-network-presets branch October 20, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant