Dinoer

The encrypted credential directory

Where passwords live, how they reach a form without passing through your shell, and the mistake that writes them in clear text.

Your credentials live in an encrypted directory that you mount, a gocryptfs volume. Inside it they sit in plain JSON files, readable only while the volume is mounted. Dinoer reads them inside the process that drives the browser, at the moment a field is filled. The value never passes through your shell, your command history or Dinoer’s logs.

Create it, then mount it

On a Git-clone installation, the setup script creates the encrypted store and an empty mount point, at the paths set by secrets_crypt_dir and secrets_dir in dinoer.conf. --sans-chiffrement creates a plain directory instead (mode 700), for a machine without gocryptfs:

bash scripts/configurer-repertoire-chiffre.sh    # from the clone

The .deb package does not ship this script: create the gocryptfs volume yourself and set secrets_dir to its mount point. Then, on either channel, mount it before a run that needs it:

dinoer-monter-secrets                                     # .deb package
bash /opt/dinoer/scripts/monter-repertoire-chiffre.sh     # Git clone

Nothing can be read on disk until the volume is mounted.

Put credentials in it

One JSON file per site, named after its domain (target.local.json, or target.local_8443.json for a specific port), anywhere under the mounted directory:

{
  "username": "operator",
  "password": "…",
  "totp_cle": "BASE32SEED",
  "ntfy_topic": "…",
  "origines_autorisees": ["target.local"]
}

Only the keys your scenarios use need to exist. totp_cle is the base32 seed for two-factor codes; ntfy_topic is the channel on which a code sent to you can be passed on. Without --secrets, Dinoer picks the file from the domain of the page actually loaded, so a redirect to another site finds no credential. With --secrets, the file is named explicitly: it must then list the domains it may be used on in origines_autorisees, and be readable by its owner only (chmod 600), otherwise Dinoer refuses it.

Use it in a scenario

{"type": "remplir", "selecteur": "input[name=\"username\"]",
 "valeur": "depuis_secrets", "secret_cle": "username"},
{"type": "remplir", "selecteur": "input[name=\"password\"]",
 "valeur": "depuis_secrets", "secret_cle": "password"}

secret_cle is the key inside the file. The scenario names a key, never a secret, so it can live in a public repository without anything to redact.

Never do this PASS=$(jq -r ‘.password’ ~/Vaults/…/creds.json): a credential in a shell variable is in your process environment, in /proc, and possibly in your history. The encrypted directory exists to avoid exactly this.

One directory per project

Two ways, for a one-off run or for a habit.

# One-off
DINOER_SECRETS_DIR=~/Vaults/MyProject /opt/dinoer/venv/bin/python3 /opt/dinoer/shot.py \
  --url … --guide-version 1.6

# Recurring: a configuration file at the root of the project
echo '{"secrets_dir": "../MyProject-secrets"}' > ~/git/MyProject/.dinoer.conf
export DINOER_CONF=~/git/MyProject/.dinoer.conf

A relative secrets_dir is resolved from the location of the configuration file, so the setting travels with the project.

--secrets <file> names one credential file for a single run without changing any configuration, for example when the same scenario serves several accounts.

When the directory is closed

Dinoer stops with an explicit error:

SecretsFermesError — exit code 42 (encrypted directory not mounted)
SecretsNonConfigureError — exit code 43 (no credential directory configured)

If you are an agent: do not try to mount it yourself. Ask the operator.

Dinoer also protects its own writes: when its operations journal is set up inside the encrypted directory and that directory is closed, it writes to a local fallback file instead of writing in clear where the encrypted store should be.

The mistake that costs the most

An unmounted directory still exists. It is simply empty, and unencrypted.

Dinoer’s guard covers its own writes. It does not cover a credential file you create yourself, with a shell command or an editor, while the directory happens to be unmounted. You are then writing next to the encrypted store, in clear, in a directory that looks exactly right.

Before creating or changing a credential file, check that the directory is mounted: it must not be empty. This mistake cannot be undone.

In short

  • The file is read inside the browser process, never through your shell.
  • Scenarios name a key, never a secret, so they can be committed.
  • DINOER_CONF, DINOER_SECRETS_DIR or --secrets for per-project and per-account credentials.
  • Exit 42 means closed, exit 43 means never configured: ask the operator, do not mount it yourself.
  • An unmounted directory is an empty, unencrypted directory. Check before writing.