Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2,101 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Canopy

Tamanu is an open-source patient-level electronic health records system for mobile and desktop.

Canopy provides:

  • a server discovery service for the Tamanu mobile app
  • the full list of available versions of Tamanu
  • download URLs to available artifacts for active versions

and, internally:

  • a global view of server status and healthchecks
  • backup management
  • associated tooling (bestool)

This is not expected to be usefully run by any other organisation; as a public-interest non-profit, BES International publishes almost all of its software as open-source.

Get

We have a container image for linux/amd64 and linux/arm64:

ghcr.io/beyondessential/canopy:latest

Each push to main builds and publishes a new image (also tagged sha-<short> for the source commit) and triggers a pulumi deploy.

Develop

  • Install Rustup, which will install Rust and Cargo.
  • Install just command runner
  • Clone the repo via git:
$ git clone git@github.com:beyondessential/canopy.git
  • Install development dependencies:
$ just install-deps

This will install cargo-nextest, diesel CLI, and watchexec.

Quick Start

  • Create a new blank postgres database.
  • Optionally set the DATABASE_URL environment variable (if your database isn't named the default canopy):
$ export DATABASE_URL=postgres://localhost/canopy_dev
  • Run migrations:
$ just migrate
  • Build the project:
$ just check
  • Run public server:
$ cargo watch-public
  • Run private server:
$ just watch-private
  • Run other binaries:
$ cargo run --bin binary_name_here
  • Tests:
$ just test
  • Lints:
$ just lint
  • Format, lint, and test in one command:
$ just dev

Available Commands

See all available commands:

$ just --list

We recommend using Rust Analyzer or Rust Rover for development.

Migrations

  1. Create a migration:
$ just migration some_name_here
  1. Write the migration's up.sql and down.sql

  2. Run the pending migrations:

$ just migrate
  1. Test your down:
$ just migrate-redo

Download a database

You'll need to have kubectl installed and authorised.

# just download-db {database name} {kubernetes namespace} [dump file]
$ just download-db canopy canopy-prod

Public API Authentication

The public-server binary serves the public API and views, which are expected to be exposed to the internet (in production behind an ingress gateway or reverse proxy).

A client is identified by an X509 certificate presented in a header, PEM-encoded and optionally URL-encoded.

Which header carries it depends on what terminates TLS in front of the server, and is chosen by CANOPY_DEVICE_AUTH_CERT_HEADER:

  • mtls (or nginx) — the mtls-certificate header, falling back to ssl-client-cert. The default, and the live ingress path.
  • xfcc (or envoy) — the x-forwarded-client-cert header, in Envoy's format.

Exactly one header is read. The other is ignored rather than tried as a fallback, so a client that can set a header it isn't meant to cannot present an enrolled device's certificate and be resolved as that device — the certificate is a public key, not a secret, and nothing at this layer proves possession. An unrecognised value for the setting keeps the default rather than guessing.

For the same reason, an x-forwarded-client-cert chain is read from its last element, which is the one the terminating proxy appended; an element a client put there first is not trusted.

To get a certificate, run:

$ just identity

This will write the identity.crt.pem and identity.key.pem.

You can then put it in an environment variable:

$ export MTLS_CERT="$(jq -sRr @uri identity.crt.pem)"

and then use curl like:

$ curl -H "mtls-certificate: $MTLS_CERT" ...

Against a server configured for XFCC, the same certificate goes in the Envoy header instead:

$ curl -H "x-forwarded-client-cert: Cert=$MTLS_CERT" ...

In production

In production, the header should be set from a client certificate, as terminated by a reverse proxy or load balancer, and any matching header on the incoming requests should be stripped.

  • Nginx: use the $ssl_client_escaped_cert variable.
  • Caddy: use the {http.request.tls.client.certificate_pem} placeholder.
  • Envoy: enable forward_client_cert_details with set_current_client_cert_details.cert, and set CANOPY_DEVICE_AUTH_CERT_HEADER=xfcc so that header is the one trusted.

MCP

Claude Code:

$ claude mcp add --transport http canopy https://canopy.tail53aef.ts.net/api/mcp

Then ask it things like "list the servers in group X" or "which backups are overdue".

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages