# Matrix Well-Known Federation & Client Auditor (`phoenix2810/matrix-well-known-auditor`) Actor

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.

- **URL**: https://apify.com/phoenix2810/matrix-well-known-auditor.md
- **Developed by:** [Sanskar Jaiswal](https://apify.com/phoenix2810) (community)
- **Categories:** Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Matrix Well-Known Federation & Client Auditor

Audits a Matrix homeserver's service-discovery files per the [Matrix specification](https://spec.matrix.org/latest/): `/.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

```json
{
  "serverName": "matrix.org",
  "checkFederationReachability": true,
  "checkClientApiReachability": true,
  "timeoutSeconds": 10
}
```

### Example output

```json
{
  "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/server` lets a homeserver delegate federation traffic to a different host and/or port than its `server_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_name` by checking this well-known file first (falling back to SRV records, then the default port) and must be able to reach `/_matrix/key/v2/server` at the resolved authority.
- `/.well-known/matrix/client` lets 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_server` is optional.
- The declared `base_url` should 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-Origin` header 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 `serverName` AND 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.

# Actor input Schema

## `serverName` (type: `string`):

The Matrix server_name (base domain) to audit, e.g. matrix.org. The actor queries https://<serverName>/.well-known/matrix/server and /.well-known/matrix/client. HTTP and HTTPS only. Private IP ranges are blocked.

## `startUrl` (type: `string`):

Alias for 'serverName', kept for input-field consistency with the rest of this actor portfolio. If both are supplied, 'serverName' takes precedence.

## `checkFederationReachability` (type: `boolean`):

If true, after parsing the m.server delegation from the server well-known file, attempt an HTTPS GET to https://<resolved host>:<resolved port>/\_matrix/key/v2/server to confirm the federation endpoint responds. The fetch target is derived from the domain's own well-known content but is still re-validated through the same SSRF-safe guard.

## `checkClientApiReachability` (type: `boolean`):

If true, attempt an HTTPS GET to <homeserver base_url>/\_matrix/client/versions to confirm the client-declared base_url actually serves the Matrix Client-Server API. The fetch target is re-validated through the same SSRF-safe guard.

## `timeoutSeconds` (type: `integer`):

Timeout for each HTTP request.

## Actor input object example

```json
{
  "serverName": "matrix.org",
  "checkFederationReachability": true,
  "checkClientApiReachability": true,
  "timeoutSeconds": 10
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "serverName": "matrix.org"
};

// Run the Actor and wait for it to finish
const run = await client.actor("phoenix2810/matrix-well-known-auditor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "serverName": "matrix.org" }

# Run the Actor and wait for it to finish
run = client.actor("phoenix2810/matrix-well-known-auditor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "serverName": "matrix.org"
}' |
apify call phoenix2810/matrix-well-known-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,phoenix2810/matrix-well-known-auditor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/BjxeiHgLIcfhikeL7/builds/0Ev2wX45zUeNLCDd2/openapi.json
