# azimutt: ten identical example badges and a compose file that ships its signing secret

> azimuttapp/azimutt is an Elixir and Elm tool for exploring, documenting and analysing relational schemas, self-hostable by container or Helm chart. Its README, compose file and build arguments each carry something a careful deployer needs to notice.

**azimuttapp/azimutt** — Explore, document and optimize any database

- Repository: https://github.com/azimuttapp/azimutt
- Website: https://azimutt.app
- Stars: 2,190 · Forks: 144
- Language: Elm
- License: MIT
- Published: 2026-09-30 · Updated: 2026-09-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/azimuttapp-azimutt

## The badge section shows the same example eleven times

The badge idea is good. Any public SQL file can be loaded with a URL parameter, so the schema sitting in your repository can be explorable by anyone who reads your README, and the mechanism is a link carrying a sql parameter pointing at the raw file and a name parameter for the title. The examples section then undercuts it. After the words here are some examples there are eleven links, and every one of them is the same URL, pointing at this repository's own structure file with the same name parameter. The sentence after them, suggesting an icon set for other colours or a custom button image, is followed by one more identical link. So the section demonstrates the mechanism once and then repeats it, and a reader looking for a second example of a different schema finds the first one again.

## The demo link carries a token in the query string

Near the top of the document there is one link that is not a badge. It goes to the hosted application with two identifiers in the path, a project and a document, and then a query parameter called token carrying a 36-character identifier in the standard dashed form. That is a share link: it grants read access to somebody's schema and analysis without an account. It is also a credential in a URL, which means it lands in every referrer header, every browser history, every screenshot of the page and any proxy log along the way. The rest of the header block follows the same pattern of embedding live links rather than describing anything, including a Product Hunt badge whose tracking parameters contain a misspelling, the third parameter being written with the letters transposed, and a colour-scheme aware logo built from two image variants.

## The compose file ships a signing secret and warns about it

The development compose file is short and has three comments that contradict its own values. The database service sets its user and password to the same default word, next to a comment saying that for security reasons you should avoid using that user. The backend service sets its connection string with the same password inline, next to a template comment showing the format with placeholders. And it sets a fixed value for the application signing secret, next to a comment saying it can literally be anything but is generally generated randomly:

```
DATABASE_URL: "ecto://postgres:postgres@database/azimutt_dev"
SECRET_KEY_BASE: "1wOVZ9rWAqPcbVZdilZzBPLXFKNrUmLUzX0q9Z02LpOy2jVWZwa6ee4fU81tuN+W"
```

That last one is not only a default, it overrides the environment file, because values in the service's environment block take precedence over the ones loaded from that file, and the environment example ships the placeholder CHANGE_ME for exactly this variable. So the compose file wins and the placeholder never applies. One more constraint in the same file: the backend is pinned to the amd64 platform, so on an ARM machine it runs under emulation.

## The build takes payment and identity secrets as arguments

The container build declares a dozen arguments, and they are not all build settings. Alongside the language versions and the database URL there are arguments for a storage key and secret, a storage host and bucket, a host name, a GitHub client identifier and secret, a payment provider API key and its webhook signing secret, and the server mode. Anything passed that way is available to every layer after it, and it is visible in the image history. The build itself is otherwise conventional and rather old-fashioned: an Elixir and Erlang version pinned to a specific pair, a Debian slim base chosen over Alpine with a comment attributing that choice to DNS resolution problems in production, Node installed from a distribution setup script, a pinned global npm, and the Elm compiler downloaded as a compressed binary straight from a GitHub release and written into the local binary directory. A comment above it still shows an example base image from 2021.

## The Heroku guide copies root storage keys into the app

The Heroku section is four configuration commands and three bullet points, and the order matters:

```bash
HEROKU_APP=<app-name>
heroku config:set PHX_HOST=$(heroku info -s | grep "web_url" | sed 's|web_url=https://||; s|/$||')
heroku config:set S3_KEY_ID=$(heroku config:get S3_ROOT_ACCESS_KEY)
heroku config:set S3_KEY_SECRET=$(heroku config:get S3_ROOT_SECRET_KEY)
```

You set the host from the app's own web URL by scraping it out of the platform's info output, then you set the storage key identifier by reading one configuration variable and writing it into a different one, and then the secret key the same way. Both source variables are named as root keys, and the bullets that follow tell you to connect to the storage provider's dashboard with those same root credentials and create a bucket. So the documented flow ends with the account's root access keys stored as the application's own storage credentials. That works, and it is broader than it needs to be: the application only needs access to the one bucket it was told to create.

## Password auth is on by default and the gateway points at the vendor

The environment example is the most revealing file in the repository. Three authentication switches are described. Password authentication is the one left uncommented. GitHub sign-in is available with a client identifier and secret. And then a section headed not implemented yet, with the word implemented misspelled, listing sign-in providers for a business network, two consumer platforms and SAML, every line commented out. Below that sit the optional switches, and three of them deserve attention: one skips the onboarding funnel, one skips email confirmation, and one requires the address to end with a particular domain, which reads like a hosted-only restriction left in a self-host guide. The gateway variable is set to the vendor's own hosted gateway address rather than to nothing, and the storage adapter defaults to local with the object-storage alternative commented out, as are all three email adapters.

## Three runtimes, one pinned package manager, and a desktop build that is skipped

The local setup asks for a package manager, the Elm compiler and its single-page-application tooling, and optionally Phoenix and Elixir with a version manager recommended, plus a PostgreSQL instance with a named user, a named password and a named development database. The workspace manifest pins the package manager to an exact version and records a hash for it, which is stricter than most repositories manage. The start command launches three packages in parallel, an internal gateway, the backend and the editor, so a development instance is three processes rather than one. The build command excludes one path explicitly, the desktop extension, so that extension is outside the standard build even though the tree carries an extensions directory. The repository also carries a tool-version file and a pre-commit configuration, which together with the version manager recommendation is three separate answers to the same question of how to pin tool versions.

## Version 2.0.0 in the manifest and no release to pin

The workspace manifest declares version 2.0.0 and the repository has no releases at all, so the number exists only in the file that declares it. There is no tag to pin, no changelog badge and no upgrade path a reader can follow from the document. What the document does give is a positioning argument, and it is a good one: entity relationship tools have diagram interfaces that fall apart as a schema grows, data catalogues are built for governance and lineage rather than relational understanding, and database clients give you completion and a table list but no picture. From there the product is described with five verbs, design with a lightweight modelling language, explore with search everywhere, query while following foreign keys into the diagram, document with notes, tags, layouts and memos, and analyse for inconsistencies and practices.

## Conclusion

azimutt is worth a look if your schema has grown past what a diagram tool can hold and you want to search it, follow relations through actual data, and keep the documentation next to the tables. Three things to handle before you deploy it. The compose file commits a signing secret and database credentials in plaintext and contains its own warning against exactly that, so treat it as a local file and generate your own. The container build takes payment and identity secrets as build arguments, which puts them in the build layer rather than only at runtime. And the environment example enables password authentication with no email confirmation while pointing the gateway at the vendor's hosted service, so decide which of those you want switched off before you put a real schema in front of strangers.

## FAQ

### What is azimutt and what is it for?

A full-stack tool for exploring, documenting and analysing relational database schemas, built with Elixir and Phoenix on the backend and Elm for the editor. It describes itself as designed for real-world databases that are large and messy, and covers design, exploration, querying, documentation and analysis.

### How do I self-host azimutt?

With a published container image and a full installation guide, or through a Helm chart with its own chart guide. There is also a Heroku template that brings up the web app, a Postgres database, object storage and a mail provider, and a development compose file for local work.

### Can I embed an azimutt schema link in my README?

Yes. Any public SQL file can be loaded with a URL parameter, and the document provides a markdown snippet for a badge carrying a sql parameter pointing at the raw file plus a name parameter for the title. The examples section that follows shows the same link eleven times rather than several different schemas.

### Which authentication methods does azimutt support?

Password authentication is enabled in the environment example, GitHub sign-in is available with a client identifier and secret, and a section headed as not yet implemented lists a business network, two consumer platforms and SAML with every line commented out. Optional switches can also skip email confirmation or require the address to end with a specific domain.

### What runtimes does azimutt need to develop?

Elixir and Phoenix for the backend and admin, Elm with its single-page-application tooling for the editor, a pinned package manager, a version manager for the Elixir toolchain, and PostgreSQL with a named user, password and development database. The workspace start command runs the gateway, the backend and the editor as three parallel processes.

## Sources

- [azimuttapp/azimutt on GitHub](https://github.com/azimuttapp/azimutt)
- [Issues](https://github.com/azimuttapp/azimutt/issues)
- [License: MIT](https://github.com/azimuttapp/azimutt/blob/main/LICENSE)
- [Project website](https://azimutt.app)
- [README](https://github.com/azimuttapp/azimutt/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/azimuttapp-azimutt
