Skip to content
Cockpit0.29.0
A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live

mesh.firewall

The host firewall, as the mesh needs it

What it is

What inbound traffic the declaration implies this machine must admit — the multicast group's port, the broadcast port when it is a permitted fallback, every declared hub port whose address is this machine's, and the server's own port when it listens beyond loopback — from the private networks the declaration names; which firewall front this host runs (ufw, nftables, the macOS application firewall); the commands that admit the plan there; what the firewall itself says about the plan now (present, missing, inactive, or unobservable without root); and what the kernel logged it dropping toward those ports in the last five minutes. Fails when a rule is observed missing or a drop was logged. Reads the host; changes nothing.

Descriptor

Canonical id
mesh.firewall
Module
mesh
Kind
query
Stability
experimental
Provenance
declared
Source
apps/majordomus-cli/src/capability/builtin/mesh.rs
Cache
disabled
Benchmark
waived: external_dependency
Tags
meshdiagnosticsfirewall

Run

query
A query. It reads and changes nothing.

The port this checkout's server listens on beyond loopback, when it does: a peer dials it there on the link protocol, so it needs admitting too. Omitted, the plan holds the discovery ports and the hub ports the declaration gives this machine.

GET /api/v1/mesh/firewall

Projections

InterfaceAsWhere
MCP toolmajordomus_mesh_firewall-
MCP resourcenot exposed-
HTTPGET /api/v1/mesh/firewallSwagger
Command linemajordomus mesh firewall-

Schemas

Input · FirewallInput
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "port": {
      "type": [
        "integer",
        "null"
      ],
      "format": "uint16",
      "minimum": 0,
      "maximum": 65535,
      "description": "The port this checkout's server listens on beyond loopback, when it does: a peer\ndials it there on the link protocol, so it needs admitting too. Omitted, the plan\nholds the discovery ports and the hub ports the declaration gives this machine."
    }
  },
  "description": "The input of `mesh.firewall` and `mesh.firewall.apply`.",
  "title": "FirewallInput"
}
Output · FirewallReport
{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean",
      "description": "Whether the host admits the mesh as far as it can be observed: nothing missing, and\nnothing logged as dropped."
    },
    "platform": {
      "type": "string",
      "description": "The platform (`linux`, `macos`, …)."
    },
    "backend": {
      "$ref": "#/$defs/Backend",
      "description": "The firewall front found."
    },
    "executable": {
      "type": [
        "string",
        "null"
      ],
      "description": "The executable the server runs as, which is what the macOS firewall admits."
    },
    "plan": {
      "$ref": "#/$defs/FirewallPlan",
      "description": "What the declaration implies this host must admit."
    },
    "commands": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/AdmitCommand"
      },
      "description": "The commands that admit it on this backend; what `apply` runs."
    },
    "observation": {
      "$ref": "#/$defs/FirewallObservation",
      "description": "The firewall's own word about the plan."
    },
    "window_seconds": {
      "type": "integer",
      "format": "uint64",
      "minimum": 0,
      "description": "The window `blocked_recently` covers, in seconds."
    },
    "blocked_recently": {
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": {
        "type": "integer",
        "format": "uint64",
        "minimum": 0
      },
      "description": "Drops the kernel logged toward the plan's ports within the window, per port;\nabsent when the log could not be read."
    },
    "verdict": {
      "type": "string",
      "description": "The sentence a person reads."
    }
  },
  "required": [
    "ok",
    "platform",
    "backend",
    "plan",
    "commands",
    "observation",
    "window_seconds",
    "verdict"
  ],
  "description": "The whole report: the plan, the backend, the commands, the firewall's word and the\nkernel's, with one verdict sentence and one `ok`. What `mesh.firewall` answers and\nwhat the doctor's `firewall` check reads.\n\n```\nuse majordomus_cli::mesh::firewall::{report, FirewallReport, BLOCK_WINDOW_SECONDS};\n\nlet r: FirewallReport = report(None, &[], None);\nassert!(r.ok && r.commands.is_empty());\nassert_eq!(r.window_seconds, BLOCK_WINDOW_SECONDS);\nassert_eq!(r.platform, std::env::consts::OS);\n```",
  "title": "FirewallReport",
  "$defs": {
    "Backend": {
      "oneOf": [
        {
          "type": "string",
          "const": "ufw",
          "description": "Uncomplicated Firewall, the Debian and Ubuntu front: `ufw` is on the path."
        },
        {
          "type": "string",
          "const": "nftables",
          "description": "nftables without ufw in front: `nft` is on the path."
        },
        {
          "type": "string",
          "const": "application_firewall",
          "description": "macOS: the application firewall (`socketfilterfw`), which admits executables, not ports."
        },
        {
          "type": "string",
          "const": "none",
          "description": "No firewall front this executable knows; nothing is observed and nothing is applied."
        }
      ],
      "description": "The firewall front this host runs, as far as the executable can tell without privilege:\ndecided by `detect` from the platform and the path, and the thing `render`,\n`observe` and `apply` are specific to.\n\n```\nuse majordomus_cli::mesh::firewall::Backend;\n\nassert_eq!(serde_json::to_value(Backend::ApplicationFirewall).unwrap(), \"application_firewall\");\nassert_eq!(Backend::None.as_str(), \"none\");\n```"
    },
    "FirewallPlan": {
      "type": "object",
      "properties": {
        "local_addresses": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "This machine's addresses, as the plan saw them."
        },
        "networks": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The private networks peers come from: the enclosing ranges of every address the\ndeclaration names, or of this machine's own when it names none."
        },
        "hub_ports": {
          "type": "array",
          "items": {
            "type": "integer",
            "format": "uint16",
            "minimum": 0,
            "maximum": 65535
          },
          "description": "The ports of the declared hubs this machine is."
        },
        "rules": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/FirewallRule"
          },
          "description": "The admissions, in a fixed order."
        }
      },
      "required": [
        "local_addresses",
        "networks",
        "hub_ports",
        "rules"
      ],
      "description": "What the declaration implies this host must admit: the rules, the networks they admit\nfrom, the hub ports this machine is listed for, and the addresses the plan saw. The\nanswer of `plan`; an empty `rules` is \"nothing is needed\", which is the answer for no\ndeclaration and for a disabled one.\n\n```\nuse majordomus_cli::mesh::firewall::{plan, FirewallPlan};\n\nlet nothing: FirewallPlan = plan(None, &[], None);\nassert!(nothing.rules.is_empty() && nothing.hub_ports.is_empty() && nothing.networks.is_empty());\n```"
    },
    "FirewallRule": {
      "type": "object",
      "properties": {
        "role": {
          "$ref": "#/$defs/Role",
          "description": "Why."
        },
        "protocol": {
          "$ref": "#/$defs/Protocol",
          "description": "The transport protocol."
        },
        "port": {
          "type": "integer",
          "format": "uint16",
          "minimum": 0,
          "maximum": 65535,
          "description": "The destination port."
        },
        "destination": {
          "type": [
            "string",
            "null"
          ],
          "description": "The destination address when it is a multicast group; `None` for this host's own."
        },
        "sources": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The networks the traffic may come from, as CIDR; empty admits any source, which is\nonly ever the case for the multicast group, whose TTL keeps it on the segment."
        },
        "reason": {
          "type": "string",
          "description": "The sentence a person reads beside the rule."
        }
      },
      "required": [
        "role",
        "protocol",
        "port",
        "sources",
        "reason"
      ],
      "description": "One inbound admission the mesh needs on this host: a protocol and a port, the group it is\naddressed to when it is multicast, the networks it may come from, and the sentence that\nsays why. A rule is derived, never written by hand; `FirewallRule::label` is how a\nverdict names it.\n\n```\nuse majordomus_cli::mesh::firewall::{FirewallRule, Protocol, Role};\n\nlet rule = FirewallRule {\n    role: Role::Hub,\n    protocol: Protocol::Tcp,\n    port: 8791,\n    destination: None,\n    sources: vec![\"192.168.7.0/24\".into()],\n    reason: \"this machine is a declared rendezvous hub\".into(),\n};\nassert_eq!(rule.label(), \"tcp/8791 from 192.168.7.0/24\");\n```"
    },
    "Role": {
      "oneOf": [
        {
          "type": "string",
          "const": "multicast",
          "description": "The declared multicast group: every runtime of the segment advertises to it."
        },
        {
          "type": "string",
          "const": "broadcast",
          "description": "The declared broadcast port, when broadcast is a permitted fallback."
        },
        {
          "type": "string",
          "const": "hub",
          "description": "This machine is a declared rendezvous hub: the fleet registers here."
        },
        {
          "type": "string",
          "const": "link",
          "description": "This machine's server is dialed by peers on the link protocol."
        }
      ],
      "description": "Why a rule exists: which part of the declaration implied it. The role is what a person\nreads beside the rule and what a verdict names, and it is serialised in snake case.\n\n```\nuse majordomus_cli::mesh::firewall::Role;\n\nassert_eq!(serde_json::to_value(Role::Hub).unwrap(), \"hub\");\nlet back: Role = serde_json::from_value(serde_json::json!(\"multicast\")).unwrap();\nassert_eq!(back, Role::Multicast);\n```"
    },
    "Protocol": {
      "oneOf": [
        {
          "type": "string",
          "const": "udp",
          "description": "Datagrams: discovery."
        },
        {
          "type": "string",
          "const": "tcp",
          "description": "Connections: the hub's registrations and the link protocol."
        }
      ],
      "description": "The transport protocol of a rule: discovery is datagrams, registration and the link\nprotocol are connections. Serialised as the word a firewall front takes.\n\n```\nuse majordomus_cli::mesh::firewall::Protocol;\n\nassert_eq!(serde_json::to_value(Protocol::Udp).unwrap(), \"udp\");\nassert_eq!(serde_json::to_value(Protocol::Tcp).unwrap(), \"tcp\");\n```"
    },
    "AdmitCommand": {
      "type": "object",
      "properties": {
        "rule": {
          "type": "string",
          "description": "The rule it admits."
        },
        "argv": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The program and its arguments, as executed."
        },
        "line": {
          "type": "string",
          "description": "The same, quoted for a shell."
        }
      },
      "required": [
        "rule",
        "argv",
        "line"
      ],
      "description": "One command that admits a rule: the rule's label, its argument vector exactly as\n`apply` executes it, and the same quoted as one line a person pastes into a root\nshell. The two are projections of one vector, so what the report shows is what runs.\n\n```\nuse std::net::Ipv4Addr;\nuse majordomus_cli::mesh::firewall::{plan, render, AdmitCommand, Backend};\nuse majordomus_cli::mesh::MeshConfig;\n\nlet config: MeshConfig = serde_json::from_value(serde_json::json!({\n    \"schema\": \"mesh/v1\", \"kind\": \"mesh-declaration\", \"id\": \"doc\", \"enabled\": true,\n})).unwrap();\nlet p = plan(Some(&config), &[Ipv4Addr::new(10, 0, 0, 2)], None);\nlet commands: Vec<AdmitCommand> = render(Backend::Ufw, &p, None);\nassert_eq!(commands[0].argv[0], \"ufw\");\nassert_eq!(commands[0].rule, \"udp/7741 to 239.255.77.77 from any\");\nassert_eq!(commands[0].line, \"ufw allow in proto udp to 239.255.77.77 port 7741 comment 'majordomus mesh: multicast discovery'\");\n```"
    },
    "FirewallObservation": {
      "type": "object",
      "properties": {
        "state": {
          "$ref": "#/$defs/FirewallObservationState",
          "description": "The verdict."
        },
        "detail": {
          "type": "string",
          "description": "The evidence: the status line, the rules found, or the refusal."
        },
        "missing": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The rules not admitted, by label, when `state` is `missing`."
        }
      },
      "required": [
        "state",
        "detail"
      ],
      "description": "The firewall's own word, judged against the plan: the state, the evidence behind it,\nand the labels of the rules not admitted when the state is `missing`.\n\n```\nuse majordomus_cli::mesh::firewall::{judge_ufw, plan, FirewallObservation, FirewallObservationState};\n\nlet o: FirewallObservation = judge_ufw(\"Status: inactive\\n\", &plan(None, &[], None));\nassert_eq!(o.state, FirewallObservationState::Inactive);\nassert!(o.missing.is_empty() && o.detail.contains(\"inactive\"));\n```"
    },
    "FirewallObservationState": {
      "oneOf": [
        {
          "type": "string",
          "const": "no_backend",
          "description": "No backend to ask."
        },
        {
          "type": "string",
          "const": "inactive",
          "description": "The firewall is off: nothing filters, nothing is needed."
        },
        {
          "type": "string",
          "const": "present",
          "description": "Every rule of the plan is admitted."
        },
        {
          "type": "string",
          "const": "missing",
          "description": "At least one rule of the plan is not admitted."
        },
        {
          "type": "string",
          "const": "unobservable",
          "description": "The firewall could not be asked from this process — it needs root."
        }
      ],
      "description": "What the host's firewall says about the plan, as one word. Only `missing` is a failure\non its own; `unobservable` is an absence that the kernel log may still turn into one.\n\n```\nuse majordomus_cli::mesh::firewall::FirewallObservationState;\n\nassert_eq!(serde_json::to_value(FirewallObservationState::Unobservable).unwrap(), \"unobservable\");\n```"
    }
  }
}

Examples

These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.

declared
{}