exochain-node 0.2.0-beta

EXOCHAIN distributed node — single binary for joining and participating in the constitutional governance network
# Copyright 2026 Exochain Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0

[package]
name = "exochain-node"
description = "EXOCHAIN distributed node — single binary for joining and participating in the constitutional governance network"
documentation = "https://docs.exochain.io"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
publish.workspace = true

[[bin]]
name = "exochain"
path = "src/main.rs"

[lints]
workspace = true

[dependencies]
# Workspace crates
exo-core = { package = "exochain-core", path = "../exo-core", version = "=0.2.0-beta" }
exo-dag = { package = "exochain-dag", path = "../exo-dag", version = "=0.2.0-beta" }
exo-gatekeeper = { package = "exochain-gatekeeper", path = "../exo-gatekeeper", version = "=0.2.0-beta" }
exo-gateway = { package = "exochain-gateway", path = "../exo-gateway", version = "=0.2.0-beta", default-features = false }
exo-api = { package = "exochain-api", path = "../exo-api", version = "=0.2.0-beta" }
exo-governance = { package = "exochain-governance", path = "../exo-governance", version = "=0.2.0-beta" }
exo-identity = { package = "exochain-identity", path = "../exo-identity", version = "=0.2.0-beta" }
exo-consent = { package = "exochain-consent", path = "../exo-consent", version = "=0.2.0-beta" }
exo-escalation = { package = "exochain-escalation", path = "../exo-escalation", version = "=0.2.0-beta" }
exo-legal = { package = "exochain-legal", path = "../exo-legal", version = "=0.2.0-beta" }
exo-avc = { package = "exochain-avc", path = "../exo-avc", version = "=0.2.0-beta" }
exo-authority = { package = "exochain-authority", path = "../exo-authority", version = "=0.2.0-beta" }
exo-economy = { package = "exochain-economy", path = "../exo-economy", version = "=0.2.0-beta" }
exo-root = { package = "exochain-root", path = "../exo-root", version = "=0.2.0-beta" }

# DAG DB MCP gateway proxy client (P1-B SDK). The default node compiles the
# governed gateway proxy transport so MCP DAG DB tools can call the production
# gateway path when runtime config is present. Missing runtime config still
# fails closed; it never falls back to fabricated packet/writeback/import/export
# results.
exochain-sdk = { package = "exochain-sdk", path = "../exochain-sdk", version = "=0.2.0-beta", optional = true }

# CLI
clap = { version = "4", features = ["derive"] }

# Configuration
serde = { workspace = true }
serde_json = { workspace = true }
chrono = { workspace = true }
toml = "0.9"

# Storage
rusqlite = { version = "0.32", features = ["bundled"] }
sqlx = { workspace = true }

# Async runtime
tokio = { workspace = true }

# Logging
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

# Crypto (identity bootstrap)
ed25519-dalek = { workspace = true }
blake3 = { workspace = true }
bs58 = { workspace = true }
rand = { workspace = true }
sha2 = { workspace = true }
hmac = { workspace = true }
zeroize = { workspace = true }
x25519-dalek = { workspace = true }
base64 = "=0.22.1"
cms = { version = "=0.3.0-pre.2", default-features = false, features = ["std"] }
const-oid = "=0.10.2"
der = { version = "=0.8.0", features = ["std"] }
ring = "=0.17.14"
x509-cert = { version = "=0.3.0-rc.4", default-features = false, features = ["std"] }

# Data directory
directories = "6"

# Serialization (CBOR for DagNode persistence)
ciborium = { workspace = true }

# HTTP (metrics route, MCP SSE transport)
axum = { workspace = true }
async-stream = { workspace = true }
tower = { workspace = true }

# Error handling
thiserror = { workspace = true }
anyhow = { workspace = true }

# P2P networking
libp2p-core = { version = "0.43", features = ["serde"] }
libp2p-gossipsub = "0.49"
libp2p-identify = "0.47"
libp2p-identity = { version = "0.2", features = ["ed25519", "peerid", "rand"] }
libp2p-kad = { version = "0.48", features = ["serde"] }
libp2p-noise = "0.46"
libp2p-ping = "0.47"
libp2p-quic = { version = "0.13", features = ["tokio"] }
libp2p-swarm = { version = "0.47", features = ["macros", "tokio"] }
libp2p-tcp = { version = "0.44", features = ["tokio"] }
libp2p-yamux = "0.47"
futures = "0.3"

# Hex encoding for peer display
hex = { workspace = true }

# Secure random token generation
getrandom = { workspace = true }

# UUID for challenge IDs
uuid = { workspace = true }

# HTTP client for Telegram bot API
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }

# JSON Schema validation for MCP tool input params (A-020). default-features
# disabled to drop the built-in HTTP retriever — we never resolve $ref over
# the network; schemas are local and compiled at registration time.
jsonschema = { version = "0.19", default-features = false }

[dev-dependencies]
tempfile = "3"

[package.metadata.cargo-machete]
# Pre-existing workspace deps used via re-exports or conditional compilation.
ignored = ["ed25519-dalek", "exo-consent", "exo-governance", "exo-identity", "zeroize", "exochain-sdk"]

[features]
# Default node builds compile the gateway production DB-backed DAG DB path and
# the MCP gateway proxy transport. Explicit `--no-default-features` node builds
# keep both paths out for minimal test/dev compatibility only.
default = ["exo-gateway/default", "dagdb-gateway-proxy"]

# GAP-012 P1-C: DAG DB MCP gateway proxy. When runtime config is absent, the
# `dagdb_*` MCP tools still register but fail closed with a structured
# `dagdb_adapter_unconfigured` result before any HTTP request. When runtime
# config is present, the node uses the `DagDbHttpClient` proxy path and
# `NodeContext` carries the operator-configured gateway endpoint, bearer token,
# tenant, and namespace.
dagdb-gateway-proxy = ["dep:exochain-sdk", "exochain-sdk/http-client"]

# GAP (Onyx pass 3, RED #1): the HTTP endpoint
# `POST /api/v1/governance/validators` used to mutate the validator set
# unilaterally on presentation of the node's admin bearer token. That
# direct mutation path has been removed: even when this feature is enabled,
# the endpoint validates the request and submits a canonical
# `ValidatorSetChange` DAG proposal for consensus instead of editing
# reactor state or persistence directly.
#
# The endpoint remains default-off because full validator-set lifecycle
# governance still requires an approved quorum-vote/commit application path.
# Do NOT enable this in production unless the surrounding operator workflow
# explicitly accepts that this HTTP route is proposal-only, not a complete
# governance lifecycle.
unaudited-admin-governance-shortcut = []

# CrossChecked receipt anchoring was introduced as a convenience adapter for
# customer-zero integration work. The route mints a node-signed TrustReceipt
# from external CrossChecked metadata, but this crate does not yet have a
# trusted CrossChecked authority resolver, proof fetcher, or tenant/workspace
# authorization contract. The production default is therefore fail-closed.
#
# Do NOT enable this in production. Enabling it means: "I accept that
# POST /api/v1/receipts can anchor CrossChecked metadata under the legacy,
# unaudited adapter contract and does not prove the external receipt or
# authority chain."
unaudited-crosschecked-receipt-anchor = []

# GAP (Onyx pass 3, RED #2 and follow-on trust-surface scans): several
# MCP tools return truthy-shaped responses without persisting to any
# store, invoking the reactor, appending to the DAG, delivering a
# message, or recording consent/escalation state — pure simulation.
# Combined with the MCP middleware's hardcoded-true constitutional
# context, this means AI agents calling these tools get "success"
# signals for actions that have zero governance-fabric effect.
#
# Mitigation: tools don't mutate real state, so blast radius is
# "AI agents acting on false success signals" — not state
# corruption. But signalling truth-that-isn't is itself a
# constitutional violation (HumanOverride, evidentiary integrity).
#
# The real fix is to wire handlers to the reactor propose/vote/
# commit flow via exo-dag, or replace with read-only helpers, or
# rename + disclaim. Until a direction is picked, we REFUSE by
# default and gate the simulators behind this explicit flag so
# no caller accidentally relies on fake-success semantics.
#
# Do NOT enable this in production. Enabling it means: "I accept
# that these MCP tool calls return truthy-shaped JSON while doing
# nothing on-chain."
unaudited-mcp-simulation-tools = []

# ONYX-4 RED R1: `POST /api/v1/0dentity/claims` is the first-touch
# identity-claim bootstrap path. Before R7, this path allowed a caller
# with only the node write bearer token to create a pending claim for any
# DID, then later session flows had no cryptographic key binding. R7
# now binds OTP sessions to a verified Ed25519 bootstrap key, but the
# first-touch product design still needs an approved proof-of-possession
# contract (for example did:exo:<bs58(blake3(pubkey))> derivation match
# plus signed bootstrap payload) before the open claim-creation path is
# safe to expose.
#
# Until that design is accepted and implemented, the endpoint refuses by
# default. Enable this only for a controlled dev cluster that explicitly
# accepts the unaudited first-touch onboarding behavior.
#
# Do NOT enable this in production. Enabling it means: "I accept that
# first-touch 0dentity claim creation is running under the legacy,
# unaudited onboarding contract tracked by Onyx-4 R1."
unaudited-zerodentity-first-touch-onboarding = []

# ONYX-4 RED R5: infrastructure Holons currently build kernel adjudication
# contexts with sentinel authority/provenance signatures (`vec![1, 2, 3]`) and
# no public key. The Holons are recommendation-only today, but the runtime must
# not start by default until the context carries real signed authority and
# provenance.
#
# Do NOT enable this in production. Enabling it means: "I accept that
# infrastructure Holons run with the unaudited R5 adjudication context tracked
# by fix-onyx-4-r5-holons-stub-context.md."
unaudited-infrastructure-holons = []

# ONYX-4 RED R3: the 0dentity device fingerprint and behavioral biometric
# modules have deterministic helpers and tests, but there is no public
# ingestion path that persists client-collected samples into the store. The
# onboarding UI can collect hashes and the scoring engine can consume samples,
# yet POST /api/v1/0dentity/claims ignores those fields.
#
# Do NOT enable this in production. Enabling it means: "I accept that the
# device_trust and behavioral_signature axes are running under the unaudited R3
# contract tracked by fix-onyx-4-r3-unwired-axes.md."
unaudited-zerodentity-device-behavioral-axes = []