atagen

malice

public
Star0
Watch0
d2a34d0f48616d4e9f590c082ba9f3f73091e402
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: README.md

973f8fbdaf80atagen2026-09-28 09:10 UTC1
# Malice
973f8fbdaf80atagen2026-09-28 09:10 UTC2
973f8fbdaf80atagen2026-09-28 09:10 UTC3
Malice is a hosted Matrix client with a Rust backend built on macltok and a
973f8fbdaf80atagen2026-09-28 09:10 UTC4
Svelte frontend. The Nix package serves both the API and the built frontend.
973f8fbdaf80atagen2026-09-28 09:10 UTC5
973f8fbdaf80atagen2026-09-28 09:10 UTC6
## NixOS module
973f8fbdaf80atagen2026-09-28 09:10 UTC7
973f8fbdaf80atagen2026-09-28 09:10 UTC8
Add this repository to your NixOS flake inputs, then include its module:
973f8fbdaf80atagen2026-09-28 09:10 UTC9
973f8fbdaf80atagen2026-09-28 09:10 UTC10
```nix
973f8fbdaf80atagen2026-09-28 09:10 UTC11
# In your flake.nix
973f8fbdaf80atagen2026-09-28 09:10 UTC12
inputs.malice.url = "git+file:///absolute/path/to/malice";
973f8fbdaf80atagen2026-09-28 09:10 UTC13
973f8fbdaf80atagen2026-09-28 09:10 UTC14
# In the modules list for your nixosSystem:
973f8fbdaf80atagen2026-09-28 09:10 UTC15
malice.nixosModules.default
973f8fbdaf80atagen2026-09-28 09:10 UTC16
```
973f8fbdaf80atagen2026-09-28 09:10 UTC17
973f8fbdaf80atagen2026-09-28 09:10 UTC18
Use a published Git URL instead of the local path if you deploy from another
973f8fbdaf80atagen2026-09-28 09:10 UTC19
machine. For a local Git flake, add new source files to Git before rebuilding;
973f8fbdaf80atagen2026-09-28 09:10 UTC20
Nix does not include untracked files.
973f8fbdaf80atagen2026-09-28 09:10 UTC21
973f8fbdaf80atagen2026-09-28 09:10 UTC22
Enable the service in your NixOS configuration. For access from another machine,
973f8fbdaf80atagen2026-09-28 09:10 UTC23
put it behind an HTTPS reverse proxy; the service binds to localhost and uses
973f8fbdaf80atagen2026-09-28 09:10 UTC24
Secure session cookies by default:
973f8fbdaf80atagen2026-09-28 09:10 UTC25
973f8fbdaf80atagen2026-09-28 09:10 UTC26
```nix
973f8fbdaf80atagen2026-09-28 09:10 UTC27
{
973f8fbdaf80atagen2026-09-28 09:10 UTC28
  services.malice.enable = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC29
  # Optional: services.malice.port = 3000;
973f8fbdaf80atagen2026-09-28 09:10 UTC30
973f8fbdaf80atagen2026-09-28 09:10 UTC31
  services.nginx.enable = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC32
  networking.firewall.allowedTCPPorts = [ 80 443 ];
973f8fbdaf80atagen2026-09-28 09:10 UTC33
  services.nginx.virtualHosts."chat.example.org" = {
973f8fbdaf80atagen2026-09-28 09:10 UTC34
    enableACME = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC35
    forceSSL = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC36
    locations."/" = {
973f8fbdaf80atagen2026-09-28 09:10 UTC37
      proxyPass = "http://127.0.0.1:3000";
973f8fbdaf80atagen2026-09-28 09:10 UTC38
      extraConfig = ''
973f8fbdaf80atagen2026-09-28 09:10 UTC39
        proxy_set_header Host $host;
973f8fbdaf80atagen2026-09-28 09:10 UTC40
        proxy_buffering off; # Keep event-stream updates timely.
973f8fbdaf80atagen2026-09-28 09:10 UTC41
      '';
973f8fbdaf80atagen2026-09-28 09:10 UTC42
    };
973f8fbdaf80atagen2026-09-28 09:10 UTC43
  };
973f8fbdaf80atagen2026-09-28 09:10 UTC44
  security.acme.acceptTerms = true;
973f8fbdaf80atagen2026-09-28 09:10 UTC45
  security.acme.defaults.email = "you@example.org";
973f8fbdaf80atagen2026-09-28 09:10 UTC46
}
973f8fbdaf80atagen2026-09-28 09:10 UTC47
```
973f8fbdaf80atagen2026-09-28 09:10 UTC48
973f8fbdaf80atagen2026-09-28 09:10 UTC49
Replace the domain and ACME email, then rebuild your NixOS configuration as
973f8fbdaf80atagen2026-09-28 09:10 UTC50
usual. Check `systemctl status malice` and visit `https://chat.example.org/`.
973f8fbdaf80atagen2026-09-28 09:10 UTC51
The module stores the master key and sessions in `/var/lib/malice`; back up that
973f8fbdaf80atagen2026-09-28 09:10 UTC52
entire directory together. Its `listenAddress`, `port`, `package`, and
973f8fbdaf80atagen2026-09-28 09:10 UTC53
`openFirewall` options can be overridden. Do not expose the HTTP listener
973f8fbdaf80atagen2026-09-28 09:10 UTC54
without a trusted TLS proxy.
973f8fbdaf80atagen2026-09-28 09:10 UTC55
973f8fbdaf80atagen2026-09-28 09:10 UTC56
## Client behavior
973f8fbdaf80atagen2026-09-28 09:10 UTC57
973f8fbdaf80atagen2026-09-28 09:10 UTC58
The home list shows non-space rooms (channels); Matrix spaces have their own
973f8fbdaf80atagen2026-09-28 09:10 UTC59
navigation and show their advertised child rooms. A Matrix homeserver is the
973f8fbdaf80atagen2026-09-28 09:10 UTC60
network address used at sign-in, not a space. Sending a message returns a queued
973f8fbdaf80atagen2026-09-28 09:10 UTC61
command, then the timeline shows whether delivery is queued, sending, confirmed
973f8fbdaf80atagen2026-09-28 09:10 UTC62
by the server, or failed. Opening a room loads its timeline; scrolling up loads
973f8fbdaf80atagen2026-09-28 09:10 UTC63
older history. Images and avatars are served through authenticated, bounded
ecb8598b38afatagen2026-10-22 09:19 UTC64
media endpoints; SVG content is not rendered. Link previews embedded by the
ecb8598b38afatagen2026-10-22 09:19 UTC65
sender always show. Otherwise the homeserver fetches previews in unencrypted
ecb8598b38afatagen2026-10-22 09:19 UTC66
rooms, and in encrypted rooms only when the user enables it in the Security
ecb8598b38afatagen2026-10-22 09:19 UTC67
panel. Room admins can disable previews per room.
973f8fbdaf80atagen2026-09-28 09:10 UTC68
973f8fbdaf80atagen2026-09-28 09:10 UTC69
The Security panel accepts incoming device-verification requests and supports
973f8fbdaf80atagen2026-09-28 09:10 UTC70
emoji/number comparison, confirmation, rejection, and recovery-key entry.
973f8fbdaf80atagen2026-09-28 09:10 UTC71
Recovery keys and encryption state are per Matrix account; preserve the local
973f8fbdaf80atagen2026-09-28 09:10 UTC72
session data when migrating an installation.
973f8fbdaf80atagen2026-09-28 09:10 UTC73
973f8fbdaf80atagen2026-09-28 09:10 UTC74
## Development
973f8fbdaf80atagen2026-09-28 09:10 UTC75
973f8fbdaf80atagen2026-09-28 09:10 UTC76
From the repository root:
973f8fbdaf80atagen2026-09-28 09:10 UTC77
973f8fbdaf80atagen2026-09-28 09:10 UTC78
```sh
973f8fbdaf80atagen2026-09-28 09:10 UTC79
nix develop
973f8fbdaf80atagen2026-09-28 09:10 UTC80
```
973f8fbdaf80atagen2026-09-28 09:10 UTC81
dffd63b58af4atagen2026-09-28 09:10 UTC82
The dev shell links `web/node_modules` from a nix build of `web/package-lock.json`,
dffd63b58af4atagen2026-09-28 09:10 UTC83
so there is no install step. After changing dependencies in `web/package.json`,
dffd63b58af4atagen2026-09-28 09:10 UTC84
refresh the lockfile with `npm --prefix web install --package-lock-only` and
dffd63b58af4atagen2026-09-28 09:10 UTC85
re-enter the shell.
dffd63b58af4atagen2026-09-28 09:10 UTC86
973f8fbdaf80atagen2026-09-28 09:10 UTC87
For live frontend changes, start these in separate terminals inside `nix develop`:
973f8fbdaf80atagen2026-09-28 09:10 UTC88
973f8fbdaf80atagen2026-09-28 09:10 UTC89
```sh
973f8fbdaf80atagen2026-09-28 09:10 UTC90
MALICE_SESSION_COOKIE_SECURE=false cargo run --bin malice
973f8fbdaf80atagen2026-09-28 09:10 UTC91
npm --prefix web run dev
973f8fbdaf80atagen2026-09-28 09:10 UTC92
```
973f8fbdaf80atagen2026-09-28 09:10 UTC93
973f8fbdaf80atagen2026-09-28 09:10 UTC94
Open `http://127.0.0.1:5173/`. Vite proxies `/api` to the Rust server on port
973f8fbdaf80atagen2026-09-28 09:10 UTC95
3000. The cookie override is for local HTTP development only. By default,
973f8fbdaf80atagen2026-09-28 09:10 UTC96
Malice accepts HTTPS homeservers; set `MALICE_ALLOW_HTTP_HOMESERVER=true` only
973f8fbdaf80atagen2026-09-28 09:10 UTC97
when testing against a trusted local HTTP homeserver.
973f8fbdaf80atagen2026-09-28 09:10 UTC98
973f8fbdaf80atagen2026-09-28 09:10 UTC99
To serve the built frontend directly from Rust instead of using Vite:
973f8fbdaf80atagen2026-09-28 09:10 UTC100
973f8fbdaf80atagen2026-09-28 09:10 UTC101
```sh
973f8fbdaf80atagen2026-09-28 09:10 UTC102
npm --prefix web run build
973f8fbdaf80atagen2026-09-28 09:10 UTC103
MALICE_SESSION_COOKIE_SECURE=false cargo run --bin malice
973f8fbdaf80atagen2026-09-28 09:10 UTC104
```
973f8fbdaf80atagen2026-09-28 09:10 UTC105
973f8fbdaf80atagen2026-09-28 09:10 UTC106
Open `http://127.0.0.1:3000/`. Rust uses `web/dist` unless `MALICE_WEB_DIR`
973f8fbdaf80atagen2026-09-28 09:10 UTC107
points at another built frontend directory. Local session data defaults to
973f8fbdaf80atagen2026-09-28 09:10 UTC108
`local-data/`; set `MALICE_DATA_DIR` to use another directory. Keep its
973f8fbdaf80atagen2026-09-28 09:10 UTC109
`master.key` and session files private.
973f8fbdaf80atagen2026-09-28 09:10 UTC110
dffd63b58af4atagen2026-09-28 09:10 UTC111
### Storybook
dffd63b58af4atagen2026-09-28 09:10 UTC112
dffd63b58af4atagen2026-09-28 09:10 UTC113
Component stories live next to their components as `web/src/**/*.stories.ts`.
dffd63b58af4atagen2026-09-28 09:10 UTC114
Storybook runs inside `nix develop`:
dffd63b58af4atagen2026-09-28 09:10 UTC115
dffd63b58af4atagen2026-09-28 09:10 UTC116
```sh
dffd63b58af4atagen2026-09-28 09:10 UTC117
cd web
dffd63b58af4atagen2026-09-28 09:10 UTC118
npm run storybook
dffd63b58af4atagen2026-09-28 09:10 UTC119
npm run build-storybook
dffd63b58af4atagen2026-09-28 09:10 UTC120
```
dffd63b58af4atagen2026-09-28 09:10 UTC121
dffd63b58af4atagen2026-09-28 09:10 UTC122
Open `http://localhost:6006/`. While it runs, `@storybook/addon-mcp` serves an
dffd63b58af4atagen2026-09-28 09:10 UTC123
MCP endpoint at `http://localhost:6006/mcp`, which the repository `.mcp.json`
dffd63b58af4atagen2026-09-28 09:10 UTC124
registers for Claude Code in this project only.
dffd63b58af4atagen2026-09-28 09:10 UTC125
973f8fbdaf80atagen2026-09-28 09:10 UTC126
## Testing
973f8fbdaf80atagen2026-09-28 09:10 UTC127
dffd63b58af4atagen2026-09-28 09:10 UTC128
In `nix develop`:
973f8fbdaf80atagen2026-09-28 09:10 UTC129
973f8fbdaf80atagen2026-09-28 09:10 UTC130
```sh
973f8fbdaf80atagen2026-09-28 09:10 UTC131
cargo fmt -- --check
973f8fbdaf80atagen2026-09-28 09:10 UTC132
cargo clippy --all-targets -- -D warnings
973f8fbdaf80atagen2026-09-28 09:10 UTC133
cargo test --all-targets
dffd63b58af4atagen2026-09-28 09:10 UTC134
npm --prefix web run check
dffd63b58af4atagen2026-09-28 09:10 UTC135
npm --prefix web test
973f8fbdaf80atagen2026-09-28 09:10 UTC136
npm --prefix web run build
973f8fbdaf80atagen2026-09-28 09:10 UTC137
```
973f8fbdaf80atagen2026-09-28 09:10 UTC138
973f8fbdaf80atagen2026-09-28 09:10 UTC139
The Rust integration test uses a mocked Matrix homeserver; it does not require
973f8fbdaf80atagen2026-09-28 09:10 UTC140
live account credentials. To verify the deployable package and NixOS module:
973f8fbdaf80atagen2026-09-28 09:10 UTC141
973f8fbdaf80atagen2026-09-28 09:10 UTC142
```sh
973f8fbdaf80atagen2026-09-28 09:10 UTC143
nix build .#server
973f8fbdaf80atagen2026-09-28 09:10 UTC144
nix flake check
973f8fbdaf80atagen2026-09-28 09:10 UTC145
```
973f8fbdaf80atagen2026-09-28 09:10 UTC146
973f8fbdaf80atagen2026-09-28 09:10 UTC147
`nix build` creates `result/bin/malice` with the frontend bundled. On a NixOS
973f8fbdaf80atagen2026-09-28 09:10 UTC148
host, `curl -fsS http://127.0.0.1:3000/api/health` checks the local backend;
973f8fbdaf80atagen2026-09-28 09:10 UTC149
opening the proxied URL checks the frontend and TLS setup.