FederationWeb is a web interface for browsing and managing a Federated Database server.
FederationWeb connects to a server that implements the Open Federated Database (OFD) specification and turns its records into pages. Operators can sign in with their access token to review reports, evidence, blacklist records, entities and audit logs and to act on them. Anonymous visitors see the records the server makes public.
FederationWeb is a client only: it stores no records of its own, and the server enforces every permission. It is built on FederationLib and served by DynamicalWeb.
FederationWeb is distributed as a Nosial Code Compiler (ncc) package that DynamicalWeb serves. Most deployments do not need to build it by hand. The Dockerfile builds the package and installs it into the DynamicalWeb image (see Deploying with Docker).
To build and run FederationWeb outside of Docker, you need:
- ncc, to build and install the package;
- PHP 8.4.1 or newer (the version current ncc requires) with the
curlandmemcachedextensions; - Node.js and npm, to compile the stylesheets, scripts and vendor assets;
- a memcached server, for sessions and the cache;
- FederationLib
2.1.0or newer, whose client the package is built with; - a Federated Database server that implements version
2.0,2.1or2.2of the OFD specification, such as FederationLib2.0.0or newer.
Note
Different major versions of the OFD interface do not interoperate. FederationWeb refuses a server whose api_version
has another major version (for example 1.0) and shows an error saying the version is unsupported.
First, install the project's dependencies:
ncc project installThen build the web_release configuration, which is the one meant for deployment:
ncc build --configuration web_releaseThe build runs npm install and npm run build before compiling, so the assets are always up to date. The package is
written to target/web_release/net.nosial.federationweb.ncc.
The stylesheets are written in SCSS under bootstrap/scss/. The compiled files in src/WebResources/css/ are not
tracked by git. When you work on the stylesheets or scripts, you can compile them without building the whole package:
npm install
npm run buildUse npm run watch:css to recompile the stylesheet every time a SCSS file changes.
The Makefile wraps the common commands:
| Target | Description |
|---|---|
make all |
Builds the debug, release and web_release packages |
make configure |
Generates the IDE stubs for the project's dependencies (ncc project stubs) |
make clean |
Removes the build output, compiled assets and installed dependencies |
make docker-build |
Builds the docker image |
make docker-up |
Starts the services defined in docker-compose.yml |
make docker-down |
Stops the services defined in docker-compose.yml |
make docker-restart |
Restarts the services defined in docker-compose.yml |
make docker-logs |
Follows the logs of the running services |
Docker is the recommended way to deploy FederationWeb. The Dockerfile compiles the web_release package
in a builder stage and installs it into the ghcr.io/nosial/dynamicalweb image, which runs:
- nginx, which serves the web application on port
8080; - memcached (256MB), which stores sessions and the cache;
- supervisord, which keeps these services running;
- PHP-FPM with the
apcu,socketsandmemcachedextensions.
The image enables memcached, renews sessions on every request (MEMCACHED_SESSION_SLIDING=1) and caches server reads
for 60 seconds (FEDERATION_CACHE_TTL=60). It also raises PHP's upload limits to 64MB so that evidence can carry file
attachments, and its health check requests / every 30 seconds.
FederationWeb only needs to reach a Federated Database server. To deploy it for a server that already runs, set
FEDERATION_SERVER_ENDPOINT to the server's URL:
services:
app:
image: federation_web
build:
context: .
container_name: federation_web
ports:
- "8080:8080"
restart: unless-stopped
environment:
- FEDERATION_SERVER_ENDPOINT=https://federation.example.com
- FEDERATION_DISABLE_CUSTOM_HOST=1
- MEMCACHED_SESSION_SECRET=${MEMCACHED_SESSION_SECRET} # CHANGE THIS!!!
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40sStart the container, open http://localhost:8080 and sign in with an operator's access token. You can also leave the
token blank to connect anonymously.
The included docker-compose.yml runs FederationWeb together with a FederationLib server, MariaDB and Redis. This is useful for development, or for trying FederationWeb without an existing server:
docker compose up -dThe web application waits until the server's health check passes, then connects to it at
http://federation_server:7000 on the internal network. The server is also exposed on port 7000.
Caution
The compose file falls back to well-known values for FEDERATION_ACCESS_TOKEN and the database passwords. Set
FEDERATION_ACCESS_TOKEN, MYSQL_PASSWORD and MYSQL_ROOT_PASSWORD in an .env file before you expose the stack
to anyone.
FederationWeb handles operators' access tokens. Before you deploy it publicly, make sure that:
- the web application is only served over HTTPS. A reverse proxy in front of it must send the
X-Forwarded-Protoheader, so that session cookies are marked as secure; FEDERATION_DISABLE_CUSTOM_HOSTis set (see Configuring the Server Connection);MEMCACHED_SESSION_SECRETis set to a long random value;- memcached is not reachable from outside the deployment. Sessions contain operators' access tokens, and the cache contains the records those operators can read;
- sign-in attempts are rate-limited, at the reverse proxy or on the Federated Database server.
The sign-in page offers two ways to connect:
- With an access token, you sign in as the operator the token belongs to. You see the pages and actions your permissions allow (see Access and Permissions).
- Without an access token, you browse anonymously and see only what the server makes public. Set
FEDERATION_DISABLE_ANONYMOUSto require a token.
By default, the sign-in page also has a server host field, so you can connect to any Federated Database server instead of the configured one. FederationWeb keeps the chosen host in your session for as long as you stay signed in.
When the configured server cannot be reached, FederationWeb shows an error page instead of a broken page. The error
page remembers the page you asked for and lets you retry it. It does the same, saying so, when the server limits the
rate of its requests and rejects one with HTTP 429 (OFD 2.2). The same page explains a missing or invalid
FEDERATION_SERVER_ENDPOINT, and a server whose major OFD version is not supported; for the latter, it offers to sign
out.
Tip
Sessions end after MEMCACHED_SESSION_TTL seconds without activity, one hour by default. Active users stay signed
in, because the Docker image renews the session cookie on every request.
The navigation bar leads to a list page for each record type: operators, entities, reports, evidence, blacklist records and the audit log. Each list can be searched, filtered and sorted, and the current state is kept in the address. You can bookmark or share a filtered list, for example the opened reports, sorted from oldest to newest:
/reports?category_filter=opened&sort_order=asc
The available filters depend on the record type. For example, reports filter by opened, closed, automated, assigned or unassigned, evidence filters by automated or not automated, and blacklist records filter by incident type. Every record has a detail page that shows its fields, its related records, its attachments and the audit entries that concern it.
The dashboard summarizes the connected server: how many records it holds, which record types it makes public, whether it accepts illegal content reports, uploads by URL and public scan content, and the content types and concerns it scans for.
Note
When a filter has to be applied by FederationWeb rather than the server, the list reads at most 50 pages of results from the server. On very large servers such a list shows the first matching records only.
The search page (/search) searches every record type at once, and the search field in the top bar suggests matching
records as you type. Anonymous visitors can use search when the server makes at least one searchable record type
public.
An entity is anything evidence can be about: a user, a domain, a telephone number and so on. Query Entity
(/entities/query) asks the server for its assessment of an entity, given its address, UUID or SHA-256 identifier:
/entities/query?identifier=user@example.com
If the server publishes the concerns it can assess, you can choose which ones the query covers. Otherwise the server assesses its default concerns:
/entities/query?identifier=user@example.com&concerns=SPAM,PHISHING
Telephone numbers are entities of the e164.arpa host, and their pages carry a Telephone Number badge.
Operators with client permissions can submit reports from the New Report form (/reports/submit), and evidence from the
evidence list or an entity's page. Every piece of evidence has a content type, which defines the context fields the form
asks for:
- TEXT, plain text content;
- EMAIL, an email message;
- SMS, a text message;
- WEB_PAGE, the content of a web page;
- HOSTNAME, a host name;
- URL, an address.
A report is submitted together with all of its evidence in a single request. The evidence of an Illegal Content report is therefore confidential from the moment it exists. The form only offers the Illegal Content incident type when the server accepts such reports.
Evidence about a telephone number is always submitted as confidential.
You can attach files of up to 64MB to evidence you submitted, or to any evidence if you hold management permissions. If
the server publishes allow_url_uploads, you can instead give a URL, and the server retrieves the file itself.
Operators with management permissions can act on records from their detail pages:
- Reports: assign them to an operator, close them with a classification, or delete them;
- Evidence: change their tag, mark or unmark them as confidential, add them to a report, upload or delete their attachments, or delete them;
- Entities: edit their metadata, whitelist them, set or clear a relationship to another entity, clear their reputation, blacklist them, or delete them;
- Blacklist records: lift them, extend them, or delete them.
Actions that are hard to undo, such as lifting a blacklist record or changing an evidence record's confidentiality, ask for confirmation first. FederationWeb hides actions that the server would reject, such as unmarking evidence that belongs to an illegal content report.
Operators with operator permissions can create, rename, enable, disable and delete operators, set their permissions, and generate new access tokens for them. Every operator can change their own configuration: the auto-assign opt-ins for ordinary and automated reports, and the bot setting.
Warning
The server discloses an access token only once, in the response that creates it. FederationWeb shows the token on the operator's page immediately after you create the operator or generate a token. Copy it before you leave the page, because it cannot be shown again.
The root and system operators cannot be modified, so their pages show no management actions.
Every list and detail page has a printable version, at the same address followed by /print:
/reports/print?category_filter=opened
/entities/{entity_id}/print
The printable pages use a separate stylesheet suited for paper and archiving, keep the filters of the list they were opened from, and are excluded from search engines.
When the server provides an OpenAPI specification at /specification, the API Specification page
(/api-specification) renders it. This endpoint is a FederationLib feature rather than part of the OFD
specification, so FederationWeb checks for it once per server and hides the page for servers that lack it.
FederationWeb starts in the dark theme. The theme toggle in the top bar switches between dark and light, and your session remembers the choice.
The interface is available in English (en), Chinese (cn), Spanish (es) and Russian (ru). On your first visit,
FederationWeb picks the language from your browser's Accept-Language header and falls back to English. The language
menu in the top bar changes it, which is the same as visiting:
/dynaweb/language/es
Translations are YAML files in src/WebLocale/. To add a language, add a file and register it under locales in
project.yml.
Every page has a description, a canonical address, Open Graph and Twitter tags and schema.org structured data. Search
engines and link previews (Discord, Slack, Telegram and other messaging apps) therefore show the page's own title and
summary. Page titles end with the connected server's name, for example Reports - Example Server.
Previews are fetched without a session, so they describe a page as an anonymous visitor sees it. A page that the server does not make public is previewed as the sign-in page. Print documents, search results and error pages ask search engines not to index them.
FederationWeb is configured entirely through environment variables. With Docker, set them in the environment section
of the service in docker-compose.yml.
FEDERATION_SERVER_ENDPOINT is the only required variable. It is the URL of the Federated Database server to connect
to, including its scheme:
environment:
- FEDERATION_SERVER_ENDPOINT=https://federation.example.comBy default, users may enter the URL of any other server on the sign-in page. This suits a FederationWeb instance that you host for yourself. On a public deployment, lock the sign-in page to the configured server:
environment:
- FEDERATION_DISABLE_CUSTOM_HOST=1Caution
The requests to a custom host are made by the FederationWeb server, not by the user's browser. On a public deployment a custom host lets anyone make your server send requests to addresses of their choosing, including addresses on your internal network.
To require an access token and disable anonymous browsing:
environment:
- FEDERATION_DISABLE_ANONYMOUS=1Note
Any non-empty value enables a flag, including 0 and false. To disable a flag, remove the variable or leave it
empty.
What anonymous visitors can see is configured on the server itself. For FederationLib, see its server.public
configuration option.
DynamicalWeb handles sessions and stores them in memcached. Signing in requires sessions, so MEMCACHED_ENABLED must be
1, true, yes or on. The Docker image sets it already.
Sessions are bound to the client's user agent and IP address with an HMAC, using MEMCACHED_SESSION_SECRET as the key.
The default secret is publicly known, so always set your own in production:
environment:
- MEMCACHED_SESSION_SECRET=a-long-random-valueA session expires after MEMCACHED_SESSION_TTL seconds (3600 by default). Because the Docker image sets
MEMCACHED_SESSION_SLIDING=1, the cookie is renewed on every request, so the TTL is really an idle timeout. With
MEMCACHED_SESSION_SLIDING=0, users are signed out one TTL after signing in, however active they are.
Set MEMCACHED_SESSION_BIND_IP=0 to keep sessions when a user's IP address changes, for example when a phone switches
between networks. The user agent is still checked.
A page reads many records from the server. Tables in particular resolve the operator and entity of every row. FederationWeb reads each record once per request, and keeps successful reads in DynamicalWeb's memcached instance, so following pages, paging through a table or reloading a page do not read them again.
Cached reads are kept separately for every server and access token, so an operator never sees records read with another operator's permissions. Submitting a form discards the cache of the server, so the next page shows the change. Changes made by other clients of the server appear once the cached reads expire.
Reads are cached for FEDERATION_CACHE_TTL seconds, 60 by default. Lower it to see changes from other clients sooner,
or set it to 0 to disable the cache:
environment:
- FEDERATION_CACHE_TTL=30If memcached is unavailable (MEMCACHED_ENABLED is not set, or the memcached extension is missing), every page reads
from the server.
Note
An access token revoked by another client of the server can keep seeing the pages it already loaded until they expire from the cache. Tokens regenerated through FederationWeb stop working immediately.
Tip
Every cache key starts with MEMCACHED_KEY_PREFIX, fw_ in the Docker image. When several web applications share
one memcached server, give each its own prefix.
| Environment Variable | Type | Default | Description |
|---|---|---|---|
FEDERATION_SERVER_ENDPOINT |
string | None (required) | The URL of the Federated Database server to connect to |
FEDERATION_DISABLE_CUSTOM_HOST |
flag | Not set | Locks the sign-in page to FEDERATION_SERVER_ENDPOINT |
FEDERATION_DISABLE_ANONYMOUS |
flag | Not set | Requires an access token to sign in |
FEDERATION_CACHE_TTL |
int | 60 |
Seconds server reads are shared between requests, 0 disables the cache |
MEMCACHED_ENABLED |
bool | 1 (Docker image) |
Enables sessions and the cache; signing in requires it |
MEMCACHED_HOST |
string | 127.0.0.1 |
The memcached server host |
MEMCACHED_PORT |
int | 11211 |
The memcached server port |
MEMCACHED_KEY_PREFIX |
string | fw_ (Docker image) |
The prefix of every cache key |
MEMCACHED_SESSION_TTL |
int | 3600 |
Seconds a session stays valid |
MEMCACHED_SESSION_SECRET |
string | dynamicalweb_default_session_secret |
The key that binds sessions to the client; set a long random value |
MEMCACHED_SESSION_SLIDING |
bool | 1 (Docker image) |
Renews the session cookie on every request, making the TTL an idle timeout |
MEMCACHED_SESSION_BIND_IP |
bool | 1 |
Set to 0 to keep sessions when the client's IP address changes |
FederationWeb shows only the pages and actions your access allows. The Federated Database server remains responsible for enforcing them:
| Access | Allows |
|---|---|
| Anonymous | Read-only access to the records the server makes public |
| Operator | Read access to the server's records, and changing your own configuration (auto-assign opt-ins and bot status) |
| Client permissions | Submitting reports and evidence, and uploading attachments to the evidence you submitted |
| Management permissions | Managing reports, evidence, entities and blacklist records, including closing and assigning reports and blacklisting |
| Operator permissions | Managing operators, their permissions, their configuration and their access tokens |
Management permissions include client permissions, so client permissions cannot be revoked from an operator that keeps management permissions.
Operator records are public: every visitor, anonymous ones included, can see the operator list and operator pages.
Entity metadata is shown to every signed-in operator, and to anonymous visitors when the server publishes
public_entity_metadata.
Besides the server's own permission checks, FederationWeb protects its users in the following ways:
- Cross-site request forgery: every form and script request that changes something must carry the session's CSRF token. A form without a valid token is sent back to its page with an error.
- Response headers: every response forbids framing (
X-Frame-Options: DENYandframe-ancestors 'none'), restricts form targets and base URLs to the application itself, disables MIME type sniffing, and sends the referrer to the same origin only. - Status messages: success and error messages are carried to the next page in the session rather than the address, so they cannot be crafted into a link.
See Preparing a Production Deployment for what a deployment must do on its side.
This project is licensed under the MIT License. See the LICENSE file for details.