100% free and open source · every tool, every host, no licence key · read the licence
Secronyx

Security

Scopes and authorization

Every tool carries one scope; registration-time policy decides which tools exist and per-request checks decide which an authenticated identity may call.

Every tool in Secronyx is registered with exactly one scope label. Authorization happens at two points: when the server starts, a scope policy decides which tools are registered at all; on the HTTP transport, every tools/call is then checked against the scopes carried by the authenticated identity. This page lists the scopes, explains both stages, and shows what a denial looks like.

The scope list

There are nineteen scopes. The counts below come from starting the current build with --scopes <name> --enable-sensitive and reading tools/list; the authoritative per-tool listing is the Tool reference.

Scope Tools What it unlocks
core 8 get_cpu_info, get_memory_info, get_disk_info, get_network_info, get_processes, get_processes_sampled, get_uptime, get_temperature
logs 5 get_journal_logs, get_syslog, get_kernel_logs, get_app_logs, get_event_log
hooks 16 scheduled tasks, cron, startup items, systemd services, kernel modules and drivers, DNS servers, routes, firewall rules, listening ports, ARP table, network stats, mounts, disk I/O, open files, inode usage
sensitive 9 get_auth_logs, get_env_vars, get_user_accounts, get_sudo_config, get_ssh_config, get_mac_status, get_certificates, get_process_environ, get_macos_tcc_permissions
hardware 25 hardware summary, USB, PCI, block devices, battery, fans, firmware, CPU topology and vulnerabilities, hwmon sensors, IOMMU, EDAC and similar
resources 11 IPC resources, namespaces, cgroups, capabilities, process tree, zombies, blocked and orphan processes, file descriptor and thread summaries, priorities
state 38 VM detection, timezone, NTP, core dumps, power state, NUMA, login sessions and history, password policy, kernel parameters, fstab, locale, systemd boot analysis and unit inventory, macOS launchd and system settings
software 115 package inventories for the OS and every language ecosystem, lock-file parsers, SBOM generation, vulnerability lookups, application detection, get_app_config, Docker containers and images, database and web servers, language runtimes and developer tooling
security 40 platform security controls: Defender, firewall profiles, BitLocker, AppLocker and WDAC, Credential Guard, RDP, WinRM and applied GPO, FileVault, Gatekeeper, SIP, XProtect, auditd, kernel lockdown, MAC detail, package repositories, auto-updates
triage 25 incident summaries: recent reboots, service failures, kernel and critical events, config changes, auth failure summary, exposed services, resource limits, posture snapshots
windows 125 registry, IIS, DCOM and COM security, Active Directory, VSS and shadow copies, WMI health, event log inventory, SMB sessions, system identity and boot, and the other Windows-only collectors
storage 23 SMART health, I/O latency, volume status, mount and filesystem events, storage deep-dive
network 25 connection tracking, DNS statistics, deep firewall analysis, Wi-Fi metrics, get_network_latency, extended network collectors
analytics 4 historical metrics, anomaly detection, capacity forecast, trend analysis
alerts 3 alert status, remediation suggestions, runbook recommendations
compliance 5 security scan, compliance check, hardening recommendations, forensic snapshot, audit trail
consumer 24 Bluetooth, audio, printers, displays and other workstation diagnostics
enhanced 27 GPU, container runtimes (Docker, Podman, Compose, Kubernetes node), kernel and memory internals (slab, vmstat, hugepages, pressure stall, OOM events, interrupts), swap and zram, WSL
report 2 generate_system_report and generate_iis_report

In total 530 tools are registered with --enable-sensitive, and 521 without it. An earlier document described a scope named sbom; the code registers those tools under software.

Stage one: registration policy

ScopePolicy in internal/mcp/authz.go is installed before any tool is registered:

type ScopePolicy struct {
    // AllowedScopes, when non-empty, is the only set of scopes whose tools
    // are registered. Empty means every scope except "sensitive".
    AllowedScopes []string

    // EnableSensitive permits registration of tools in the "sensitive"
    // scope. It is required even when AllowedScopes lists "sensitive".
    EnableSensitive bool
}

The command line maps onto it as follows:

Flag Environment variable Effect
--scopes core,logs SECRONYX_SCOPES Register only tools in the listed scopes. The flag wins when both are set.
--enable-sensitive SECRONYX_ENABLE_SENSITIVE=1 Permit registration of sensitive tools. Required even when --scopes names sensitive.

A tool the policy refuses is simply never registered. It is absent from tools/list on every transport, including stdio, and calling it returns the ordinary tool-not-found error, so a client cannot tell the difference between a tool that does not exist and one that was withheld:

{"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Tool not found","data":"get_env_vars"}}

At start-up the server logs how many tools the policy withheld, and a separate warning when sensitive tools are on:

Scope policy: 9 tools not registered (sensitive=false, scopes="")
WARNING: sensitive-scope tools are enabled

Two consequences worth knowing. First, a scope name that matches nothing registers nothing: --scopes bogus starts a server with zero tools and logs Scope policy: 530 tools not registered. Second, --scopes core,sensitive without --enable-sensitive registers the eight core tools only.

# Core metrics only
secronyx --scopes core

# Core, logs and hooks
secronyx --scopes core,logs,hooks

# Everything, including sensitive tools (both are required)
secronyx --scopes core,logs,hooks,sensitive --enable-sensitive

# The same through the environment, for a service unit
SECRONYX_SCOPES=core,logs SECRONYX_ENABLE_SENSITIVE=1 secronyx

On stdio the registration policy is the only control. Nothing in the stdio protocol carries a caller identity, so the process that spawns the server is trusted to the extent of whatever was registered. The Command line page covers the --query mode, which does not go through the registry at all.

Stage two: per-request checks

When the HTTP transport authenticates a request it places an Identity in the request context:

type Identity struct {
    Subject  string
    ClientID string
    Scopes   []string
    // Method names the authenticator that established the identity.
    Method string
}

tools/call then runs IdentityAllows(identity.Scopes, tool.Scope) before the handler executes. A grant matches when it is:

  • the wildcard *;
  • the tool's scope name itself, for example logs; or
  • the namespaced form mcp:tools:<scope>, for example mcp:tools:logs, so an authorization server whose scope namespace is shared with other resources can issue distinct values.

The bare audience value mcp:tools grants nothing on its own. An identity with an empty scope list can authenticate but can call nothing. tools/list is filtered with the same function, so a caller sees only what it may invoke; the .well-known/oauth-protected-resource document advertises scopes_supported as the scopes that map to registered tools.

A request with no identity in its context (stdio, or HTTP with no authentication configured, which the listener guardrails permit only on loopback or with --allow-unauthenticated) is governed by the registration policy alone.

Where identity scopes come from

Each authentication method produces the scope list differently. Details of configuring each are in Authentication.

Method Source of scopes
Static bearer token (--token) Always ["*"]. The static token is full access to whatever is registered.
OIDC (--oidc-issuer) The JWT's scope claim (space-separated string), else scp (array, as Azure AD issues), else scopes (array).
SaaS JWKS validator (hybrid mode) The same three claims, read by internal/agent/jwks.go.
OAuth introspection (--auth-server) The introspection response's mcp_scopes array; if absent or empty, the space-separated scope string.
Built-in token server (secronyx-token-server) Each client record's allowed_scopes becomes the token's mcp_scopes claim and is returned from /introspect as both scope and mcp_scopes.
API key (--api-keys-file) The key record's scopes, set by secronyx apikey create --scopes ... (default core). The CLI refuses to issue *.
Mutual TLS (--mtls-identity-file) The matching identity entry's scopes; if nothing matches, --mtls-default-scopes, else the certificate is denied. * is rejected when the identity file is loaded.
SSH request signing (--ssh-authorized-keys) The scopes="a,b" option on the key's line. For certificates, the CA line's scopes=; if the CA line has none, the certificate extension secronyx-scopes. Unknown options are rejected so a typo cannot widen access.

Whatever the method, sensitive in a grant only takes effect if the server was started with --enable-sensitive; a grant cannot register a tool.

What a denial looks like

A call outside the identity's grant is refused with JSON-RPC error -32003, which sits in the implementation-defined server error range. The HTTP status is 200 because the request was authenticated and well-formed; the refusal is at the JSON-RPC layer.

{"jsonrpc":"2.0","id":2,"error":{"code":-32003,"message":"Forbidden","data":"tool \"get_journal_logs\" requires scope \"logs\""}}

The denial is audited as a tools/call event with result denied and error scope denied, and counted in metrics under the error type scope_denied. Abridged — the written record also carries timestamp, seq, event_id and the hash-chain fields:

{"action":"tools/call","resource":"get_journal_logs","identity":"demo","client_ip":"127.0.0.1","params":{"lines":5},"result":"denied","error":"scope denied"}

Worked example with an API key

Issue a key scoped to core, start a loopback listener, and exercise both stages. Loopback is the one configuration that may run without TLS; see Remote access over HTTP for anything else.

secronyx apikey create --file ./keys.json --name demo --scopes core
# Created API key 78ef7122 for "demo" (scopes: core)
# This key is shown once and cannot be recovered:
# msk_78ef7122_...

secronyx --transport http --listen 127.0.0.1:18080 \
    --api-keys-file ./keys.json \
    --audit-output ./audit.jsonl

The key sees eight tools, may call one of them, and is refused on another:

KEY=msk_78ef7122_...

curl -s -X POST http://127.0.0.1:18080/ -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools | length'
# 8

curl -s -X POST http://127.0.0.1:18080/ -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_journal_logs","arguments":{"lines":5}}}'
# {"jsonrpc":"2.0","id":2,"error":{"code":-32003,"message":"Forbidden","data":"tool \"get_journal_logs\" requires scope \"logs\""}}

curl -s -X POST http://127.0.0.1:18080/ -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_uptime"}}'
# {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{\n  \"boot_time\": ..."}]}}

The audit file records the authentication (with the granted scopes in metadata), the denial and the success in one chain. The full walk-through, including expiry and CIDR restrictions, is in Tutorial: per-operator API keys.

Choosing a policy

  • Start with --scopes core,logs,hooks for general diagnostics and widen per host as needed. Everything else can be added without restarting clients, but the server must restart: the policy is evaluated at registration and tools registered earlier are not re-evaluated.
  • Keep sensitive off unless the host is isolated and the identity that will call those tools is individually accountable. get_env_vars and get_process_environ redact secret-looking values before returning them, but the scope as a whole — get_sudo_config, get_ssh_config, get_user_accounts, get_auth_logs — still describes how the host authenticates and authorises.
  • On HTTP, issue narrow grants per identity rather than widening the registration policy. A core-only API key on a server registered with every scope still sees eight tools.
  • Prefer mcp:tools:<scope> grants from an enterprise IdP whose scope namespace is shared, and plain names from the API key, mTLS and SSH files, which are Secronyx-specific.

For the wire format of the error see JSON-RPC API; for what the audit trail records about denials see Audit logging.

Built 2026-09-19. Source: levantar-ai/secronyx. Found a mistake? Tell us.