Matrix Well-Known Federation & Client Auditor
Pricing
Pay per usage
Matrix Well-Known Federation & Client Auditor
Deep audit of a Matrix homeserver's /.well-known/matrix/server and /.well-known/matrix/client discovery files. Validates m.server host:port syntax, optional federation port reachability, client base_url reachability, and CORS.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Sanskar Jaiswal
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Audits a Matrix homeserver's service-discovery files per the Matrix specification: /.well-known/matrix/server (Server-Server API federation delegation) and /.well-known/matrix/client (Client-Server API client discovery). Matrix is the open federated chat protocol behind Synapse, Dendrite, Conduit, and clients like Element. Misconfigured delegation is a frequent self-hosting support problem: wrong port, unreachable federation target, malformed host:port syntax, missing CORS on the client file, http:// instead of https:// in the homeserver base_url, or a client base_url that does not actually serve the Matrix client API.
Use cases
- Matrix homeserver operators (Synapse, Dendrite, Conduit) verifying federation delegation before going live or after a server move.
- Infrastructure and DevOps teams debugging "federation not working" or "client can't log in" reports.
- Agency and consultant audits of a client's self-hosted Matrix deployment.
- CI/CD pipelines validating
/.well-known/matrix/*after reverse-proxy or DNS config changes. - Protocol tooling and client developers checking real-world homeserver well-known behavior against the spec.
Input
| Field | Type | Required | Description |
|---|---|---|---|
serverName | string | yes | The Matrix server_name (base domain) to audit, e.g. matrix.org. |
checkFederationReachability | boolean | no | If true (default), attempts an HTTPS GET to https://<resolved host>:<resolved port>/_matrix/key/v2/server using the host:port parsed from m.server, to confirm federation is actually reachable. |
checkClientApiReachability | boolean | no | If true (default), attempts an HTTPS GET to <homeserver base_url>/_matrix/client/versions to confirm the declared client base URL actually serves the Matrix Client-Server API. |
timeoutSeconds | integer | no | Request timeout, 3-30 seconds. Default 10. |
Output
One JSON object per run, pushed to the default dataset.
| Field | Type | Description |
|---|---|---|
serverName | string | The audited server_name. |
checkedAt | string | ISO timestamp of the audit. |
server | object | Analysis of /.well-known/matrix/server (see below). |
client | object | Analysis of /.well-known/matrix/client (see below). |
score | integer | Readiness score, 0-100. The server half and client half are each capped at 50 points independently before being summed, so a perfect federation setup with a broken client file (or vice versa) cannot silently score above 50. |
grade | string | Letter grade, A+ to F. |
issues | array | Combined, deduplicated issue descriptions from both halves. |
recommendations | array | Combined actionable fixes, citing the specific Matrix spec requirement. |
server object
| Field | Type | Description |
|---|---|---|
url | string | The /.well-known/matrix/server URL that was fetched. |
finalUrl | string | Final URL after following any redirects. |
httpStatus | integer|null | HTTP status code. |
https | boolean | Whether served over HTTPS. |
contentType | string|null | Content-Type response header value. |
found | boolean | Whether a 200 response was served. |
jsonValid | boolean | Whether the body parses as valid JSON. |
parseError | string|null | JSON parse error, if any. |
mServerRaw | string|null | The raw m.server value from the JSON body. |
mServerHost | string|null | Parsed host from m.server. |
mServerPort | integer|null | Parsed explicit port from m.server, or null if the default (8448) applies. |
hostFormatValid | boolean | Whether m.server is syntactically a valid host[:port] value per the spec. |
isIpLiteral | boolean | Whether the host is an IPv4/IPv6 literal rather than a hostname. |
federationReachable | boolean|null | Whether /_matrix/key/v2/server responded at the delegated host:port. null if the check was disabled or not applicable. |
federationCheckError | string|null | Error from the federation reachability check, if any. |
issues | array | Issues specific to the server well-known file. |
client object
| Field | Type | Description |
|---|---|---|
url | string | The /.well-known/matrix/client URL that was fetched. |
finalUrl | string | Final URL after following any redirects. |
httpStatus | integer|null | HTTP status code. |
https | boolean | Whether served over HTTPS. |
contentType | string|null | Content-Type response header value. |
found | boolean | Whether a 200 response was served. |
jsonValid | boolean | Whether the body parses as valid JSON. |
parseError | string|null | JSON parse error, if any. |
homeserverBaseUrl | string|null | The m.homeserver.base_url value. |
homeserverBaseUrlHttps | boolean | Whether base_url uses https://. |
identityServerBaseUrl | string|null | The m.identity_server.base_url value, if present. |
identityServerPresent | boolean | Whether the optional m.identity_server member is present. |
corsAllowOrigin | string|null | The Access-Control-Allow-Origin header value on the client well-known response, if present. |
corsOk | boolean | Whether CORS is permissive (* or matches the server's own origin). |
clientApiReachable | boolean|null | Whether <base_url>/_matrix/client/versions responded. null if the check was disabled or not applicable. |
clientApiCheckError | string|null | Error from the client API reachability check, if any. |
issues | array | Issues specific to the client well-known file. |
Example input
{"serverName": "matrix.org","checkFederationReachability": true,"checkClientApiReachability": true,"timeoutSeconds": 10}
Example output
{"serverName": "matrix.org","checkedAt": "2026-10-08T12:00:00.000Z","server": {"url": "https://matrix.org/.well-known/matrix/server","https": true,"httpStatus": 200,"found": true,"jsonValid": true,"mServerRaw": "matrix-federation.matrix.org:443","mServerHost": "matrix-federation.matrix.org","mServerPort": 443,"hostFormatValid": true,"isIpLiteral": false,"federationReachable": true,"issues": []},"client": {"url": "https://matrix.org/.well-known/matrix/client","https": true,"httpStatus": 200,"found": true,"jsonValid": true,"homeserverBaseUrl": "https://matrix-client.matrix.org","homeserverBaseUrlHttps": true,"identityServerPresent": true,"corsOk": true,"clientApiReachable": true,"issues": []},"score": 100,"grade": "A+","issues": [],"recommendations": ["Matrix well-known federation and client discovery look spec-conformant. Re-run this audit after homeserver upgrades to catch regressions."]}
What the spec requires
Per the Matrix specification:
/.well-known/matrix/serverlets a homeserver delegate federation traffic to a different host and/or port than itsserver_name. The body is JSON{"m.server": "<hostname>[:<port>]"}; if the port is omitted, federation traffic defaults to port 8448. IPv6 literal hosts must be bracketed ([::1]:8448).- Other homeservers resolve
server_nameby checking this well-known file first (falling back to SRV records, then the default port) and must be able to reach/_matrix/key/v2/serverat the resolved authority. /.well-known/matrix/clientlets clients (Element, other apps) discover the homeserver's Client-Server API base URL without the user typing it manually. The body is JSON{"m.homeserver": {"base_url": "https://..."}, "m.identity_server": {"base_url": "https://..."}};m.identity_serveris optional.- The declared
base_urlshould actually serve/_matrix/client/versions; a well-known file pointing at a non-functioning base URL breaks client auto-discovery even though the file itself looks correct. - Since the client well-known file is fetched by browser-based clients (Element Web) from a different origin, the spec expects a permissive
Access-Control-Allow-Originheader so those clients can read it cross-origin.
Security
- Public HTTP/HTTPS only. URL credentials are rejected.
- Localhost and private/link-local IPv4 and IPv6 literal ranges (including bracketed IPv6 literals) are blocked.
- Hostnames are DNS-resolved and resolved addresses are re-checked against the same private-range rules before any request is made (SSRF defense in depth).
- This guard is applied both to the two well-known fetches on
serverNameAND to the federation/client targets parsed out of those files' JSON content, since those are server-controlled secondary fetch targets and the single most important security control in this actor. - Redirects are manually followed and each hop is revalidated with the same SSRF checks before the next request is made (max 3 hops).
- Response bodies are capped at 1 MB.
- Only four URLs are ever fetched: the two well-known files on
serverName, and (if enabled) the federation key endpoint at the host:port declared inside the server well-known file and the client versions endpoint at the base_url declared inside the client well-known file. No other links are followed. - No login, no credential collection, no private-data extraction.
Pricing
| Event | Price (USD) |
|---|---|
| Actor start | $0.005 |
| Domain audited | $0.01 |
Apify takes a 20% platform commission; the operator keeps 80%.
FAQ
What happens if both well-known files are missing? The audit still completes and returns a low score with a missing status on the endpoint checks. Federation can still work via SRV record fallback or the default port 8448, so a missing well-known file is not an automatic hard failure, but it is flagged as a recommendation since well-known delegation is the spec's preferred, more flexible mechanism.
Why are the server and client scores capped independently before being summed? So that a homeserver with perfect federation delegation but a broken or missing client well-known file (or vice versa) cannot score above 50/100. Each half contributes at most 50 points regardless of how well the other half performs.
Does this actor fetch arbitrary URLs found in the response? No. It only ever fetches the two well-known URLs on the audited serverName, plus the federation key endpoint and client versions endpoint derived specifically from m.server and m.homeserver.base_url — both of which are re-validated through the same SSRF guard used on the primary input before being fetched.
Why does this actor exist when generic well-known probers already check these paths? Existing bulk well-known probers on Apify Store check dozens of well-known paths shallowly and report only found/not-found. They do not validate m.server host:port syntax, do not test federation port reachability, do not confirm the client base_url actually serves the Matrix client API, and do not check CORS on the client file. This actor does one deep, spec-grounded audit of the Matrix well-known files specifically.