atagen

malice

public
Star0
Watch0
544a170ead2c7f632e7957c1ed729ba50c80ec51
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
README.mdMarkdown147 lines4.8 KB

Malice

Malice is a hosted Matrix client with a Rust backend built on macltok and a Svelte frontend. The Nix package serves both the API and the built frontend.

NixOS module

Add this repository to your NixOS flake inputs, then include its module:

# In your flake.nix
inputs.malice.url = "git+file:///absolute/path/to/malice";

# In the modules list for your nixosSystem:
malice.nixosModules.default

Use a published Git URL instead of the local path if you deploy from another machine. For a local Git flake, add new source files to Git before rebuilding; Nix does not include untracked files.

Enable the service in your NixOS configuration. For access from another machine, put it behind an HTTPS reverse proxy; the service binds to localhost and uses Secure session cookies by default:

{
  services.malice.enable = true;
  # Optional: services.malice.port = 3000;

  services.nginx.enable = true;
  networking.firewall.allowedTCPPorts = [ 80 443 ];
  services.nginx.virtualHosts."chat.example.org" = {
    enableACME = true;
    forceSSL = true;
    locations."/" = {
      proxyPass = "http://127.0.0.1:3000";
      extraConfig = ''
        proxy_set_header Host $host;
        proxy_buffering off; # Keep event-stream updates timely.
      '';
    };
  };
  security.acme.acceptTerms = true;
  security.acme.defaults.email = "you@example.org";
}

Replace the domain and ACME email, then rebuild your NixOS configuration as usual. Check systemctl status malice and visit https://chat.example.org/. The module stores the master key and sessions in /var/lib/malice; back up that entire directory together. Its listenAddress, port, package, and openFirewall options can be overridden. Do not expose the HTTP listener without a trusted TLS proxy.

Client behavior

The home list shows non-space rooms (channels); Matrix spaces have their own navigation and show their advertised child rooms. A Matrix homeserver is the network address used at sign-in, not a space. Sending a message returns a queued command, then the timeline shows whether delivery is queued, sending, confirmed by the server, or failed. Opening a room loads its timeline; scrolling up loads older history. Images and avatars are served through authenticated, bounded media endpoints; SVG content is not rendered. Link previews use URL text only and do not fetch third-party pages.

The Security panel accepts incoming device-verification requests and supports emoji/number comparison, confirmation, rejection, and recovery-key entry. Recovery keys and encryption state are per Matrix account; preserve the local session data when migrating an installation.

Development

From the repository root:

nix develop

The dev shell links web/node_modules from a nix build of web/package-lock.json, so there is no install step. After changing dependencies in web/package.json, refresh the lockfile with npm --prefix web install --package-lock-only and re-enter the shell.

For live frontend changes, start these in separate terminals inside nix develop:

MALICE_SESSION_COOKIE_SECURE=false cargo run --bin malice
npm --prefix web run dev

Open http://127.0.0.1:5173/. Vite proxies /api to the Rust server on port 3000. The cookie override is for local HTTP development only. By default, Malice accepts HTTPS homeservers; set MALICE_ALLOW_HTTP_HOMESERVER=true only when testing against a trusted local HTTP homeserver.

To serve the built frontend directly from Rust instead of using Vite:

npm --prefix web run build
MALICE_SESSION_COOKIE_SECURE=false cargo run --bin malice

Open http://127.0.0.1:3000/. Rust uses web/dist unless MALICE_WEB_DIR points at another built frontend directory. Local session data defaults to local-data/; set MALICE_DATA_DIR to use another directory. Keep its master.key and session files private.

Storybook

Component stories live next to their components as web/src/**/*.stories.ts. Storybook runs inside nix develop:

cd web
npm run storybook
npm run build-storybook

Open http://localhost:6006/. While it runs, @storybook/addon-mcp serves an MCP endpoint at http://localhost:6006/mcp, which the repository .mcp.json registers for Claude Code in this project only.

Testing

In nix develop:

cargo fmt -- --check
cargo clippy --all-targets -- -D warnings
cargo test --all-targets
npm --prefix web run check
npm --prefix web test
npm --prefix web run build

The Rust integration test uses a mocked Matrix homeserver; it does not require live account credentials. To verify the deployable package and NixOS module:

nix build .#server
nix flake check

nix build creates result/bin/malice with the frontend bundled. On a NixOS host, curl -fsS http://127.0.0.1:3000/api/health checks the local backend; opening the proxied URL checks the frontend and TLS setup.