A contract defines the endpoint checks that must pass, their latency thresholds, and optionally a saved packet regression suite. Results are explicit PASS, FAIL or UNKNOWN. Live probes originate from the MCP host, not from a branch, router or end user's machine. This is not an endpoint-agent deployment or proof of every application workflow.
Approve endpoints first
Nothing can be probed until an administrator configures named targets. Contracts reference these aliases, not arbitrary URLs, hosts, credentials or request bodies.
Dashboard setup (no JSON required)
Open Config → Edit config → Form → Service Probes → Add service, or use
Service Routing → Service probes (also available under Approved service
when drafting a policy). The focused routing editor changes only serviceProbes,
merging other settings from the live configuration on the server.
Choose a unique service ID, protocol (HTTPS, TLS, TCP or DNS), hostname, applicable port/path, and 1–32 allowed IPv4/IPv6 addresses or CIDRs. Paste addresses separated by commas or newlines. Only HTTPS targets appear in Service Routing. DNS targets only check resolution; TLS/TCP targets do not use an HTTP path.
Add to draft / Update draft is local only. Use Preview diff → Save (Save probes in the focused editor), then Keep changes before the optional auto-revert timer expires. This persists through the existing configuration backup/safe-apply workflow; no probe or router command is run by this editor. Search approvals by ID, protocol, hostname or IP. Expand a target to inspect its allowed ranges. Existing IDs cannot be renamed because contracts/policies refer to them. Removing an approval requires confirmation and saving; consumers then fail closed, but existing router routing rules are not removed.
JSON configuration
Example config fragment (documentation addresses; replace deliberately):
{
"serviceProbes": {
"timeoutMs": 5000,
"targets": {
"checkout_dns": {
"kind": "dns",
"host": "payments.example.com",
"addresses": ["203.0.113.10/32"]
},
"checkout_https": {
"kind": "https",
"host": "payments.example.com",
"port": 443,
"path": "/health",
"addresses": ["203.0.113.10/32"]
}
},
"scheduled": {}
}
}Load this through the usual --config file (or full MIKROTIK_DEVICES document).
The dashboard exposes target management and the timeout; scheduled enrollment
remains in the JSON editor. Raycast exposes the timeout. Creating a contract does not grant target access.
Use narrow IP/CIDR ranges: DNS answers outside them invalidate the check. Every
answer must be approved; the socket pins the first approved IP and never resolves
the hostname again during connection. Private ranges are permitted only when
explicitly configured by the administrator. Update approval when provider IPs change.
Create and check
In Service Health, select the owning router, contract name, approved aliases and latency threshold. Saving does not probe anything. Run checks records a new result; View history only reads existing results. The form expects HTTPS 200; use MCP for other explicitly accepted status codes.
MCP tools:
| Tool | Purpose |
|---|---|
list_service_probe_targets | Discover approved aliases and probe kinds |
create_service_contract | Save a new immutable definition |
list_service_contracts | List the selected router's definitions |
run_service_contract | Run a UUID-owned contract and persist its evidence |
service_contract_history | Read the latest 100 results for that contract |
audit_service_contracts | Check explicitly enrolled contracts for scheduled finding diffs |
{
"device": "branch",
"name": "Checkout availability",
"checks": [
{ "target": "checkout_dns", "maxLatencyMs": 500 },
{ "target": "checkout_https", "maxLatencyMs": 1500, "expectedStatus": [200, 204] }
]
}Use the returned id with run_service_contract under the same device. To
revise a definition, create a new contract and explicitly change its consumers.
Definitions are not overwritten. Device/tool access is enforced on dashboard
paths too; read-only server mode rejects definition writes. Case and contract
POST routes bound bodies to 8 KiB and reject cross-origin browser operations.
Reverse proxies should preserve the public Host when forwarding these requests.
What a passing check proves
- DNS: address resolution succeeded and all answers are inside approved ranges.
- TCP: a connection was established to the configured port. No application request or SSH login was attempted.
- TLS: a TLS handshake with certificate/hostname validation succeeded.
- HTTPS: a GET returned response headers with an expected status over validated TLS, within the latency threshold. Bodies are not collected or checked.
There is no insecure TLS switch, redirect following, cookie jar, authentication header, query string or model-controlled request body. Configure a safe health endpoint: GET is not guaranteed side-effect-free on every application. Failed connections/status/latency are FAIL; unavailable approval/resolution/evidence is UNKNOWN. Neither passes a gate. Raw exceptions, HTTP bodies and credentials are not stored. IP allowlists protect destination scope; they do not authenticate users.
Limits: 1–10 checks per contract; 100–10000 ms per probe (DNS plus connection); at most four concurrent contract runs per process, with duplicate concurrent runs rejected. Only the first approved address is connected; this does not measure every backend of an anycast/load-balanced service. DNS uses the host's configured resolver. IPv4 literals and DNS hostnames are accepted; a hostname may resolve to approved IPv6.
Optional packet regressions
Set packetSuiteId to an existing simulator suite of 1–100 packets. Each run reads
a fresh /export terse through the shared RouterOS connector. It never substitutes
an old snapshot after a change. The pure simulator evaluates the expected
accept/drop/reject results and records the export SHA. Missing exports/suites or
unmodelled packet behavior are UNKNOWN, not skipped checks. The export read obeys
the connector's connection/transport timeouts and an 8-second read budget; it is
not covered by the native endpoint probe deadline. No router configuration changes.
These are simulated firewall regressions, not live negative-connectivity proof. Combine them with the live service checks instead of treating either as complete end-to-end network verification.
Rollout gates
start_rollout accepts an optional service_contracts array:
{
"service_contracts": [{ "device": "branch", "id": "<UUID returned by create_service_contract>" }]
}This fragment accompanies the existing commands/targets/strategy parameters.
With confirm=false, no checks or changes run. Confirmed rollouts check contracts
before the first change and after every wave, including contracts on untouched
targets. Every contract owner must be in the target set and initially reachable.
Unknown/failure/storage error prevents a passing service gate. onFailure=continue
is rejected when contracts are supplied. Existing halt/revert policy remains in
charge; this does not add crash-safe orchestration or stronger rollback guarantees.
Gate history includes contract/run IDs and explicitly names the MCP-host viewpoint.
Continuous checks and notifications
Enroll up to five contract UUIDs per device under
serviceProbes.scheduled, for example {"branch": ["<contract UUID>"]}. Then
create an ordinary scheduled audit using tool audit_service_contracts, the same
device and an appropriate cron. Newly created contracts are not automatically
enrolled. The selected contracts may contain at most ten endpoint probes in total;
configure the scheduler timeout to cover their deadlines and optional export reads.
The existing scheduled-audit engine compares stable finding IDs and notifies on new/worsened/resolved findings through its configured channels. Unknown evidence is a finding; no enrollment produces an informational finding, not a false healthy result. No schedules, notifications or external services are enabled by installation.
Persistence and verification
Definitions and runs live in service_contracts / service_contract_runs tables
in the local snapshot database. Storage failure is an operation error, not a
successful recorded run. Histories show the latest 100 records; this version has
no automatic deletion, tenant isolation or immutable/WORM storage.
Run:
bunx vp test run tests/service-contracts tests/observability/bounded-request.spec.ts
bun run tests/service-contracts/store.smoke.tsTests cover approved IP ranges, mixed DNS answers, exact IP pinning, DNS-only checks, latency/status failures, unknown evidence, native localhost TCP/TLS/HTTPS failure handling, fresh-export packet regressions, history/access checks, explicit schedule enrollment, and rollback progression with fake devices. No production device mutations are used to test gates.
On 2026-09-11 the new native probe engine established TCP connections to the SSH
ports of the MCP-discovered home-ax3 and netherlands devices. No SSH login or
RouterOS command was issued by that smoke check. This verifies TCP connectivity,
not live TLS/HTTP service correctness or a production rollout. The currently running
MCP server must load this version before new tools can be invoked through it.