dxthing Get in touch

Every project · one launcher · one layout

The commands you can't remember, in one place.

Restoring last night's data, resetting the dev database, editing a secret, starting a project the way the others are: each one is a command you look up again every time. dxthing is the command hub and the janitor, and it adapts to whichever project you run it in.

Act I

Looking it up, again.

A bug only shows up with real data, so you want production's data on your machine. You've done it before. You don't remember how.

01 · Last night's data

Find the backup, then restore it.

Which bucket, which tool, which flags, which container. Every step is a search through your shell history or an old note.

~/src/shop
$ rclone ls r2:backups/shop | sort | tail -1
192938112 shop-2026-09-27T03-00.sql.gz
$ rclone copy r2:backups/shop/shop-2026-09-27T03-00.sql.gz dumps/
$ gunzip -c dumps/shop-*.sql.gz | docker exec -i db-shop mysql -uroot -p shop_dev
ERROR 1049 (42000): Unknown database 'shop_dev'

02 · A secret

Then edit a secret.

sops is great and its commands are arcane. Which key file, which path, and why won't it decrypt on this machine?

~/src/iacthing
$ sops secrets/shop.yaml
Failed to get the data key required to decrypt the SOPS file.
$ SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops secrets/shop.yaml
# right. that one. until next month.

03 · Is my .env right?

Then check the environment.

The app fails at startup because a variable is missing, and the only way to find which is to diff two files by hand.

~/src/shop
$ diff <(cut -d= -f1 .env | sort) <(cut -d= -f1 .env.example | sort)
> R2_BUCKET
> RESEND_API_KEY

04 · A new project

And start a project like the others.

Copy the last one, rename things, and try to remember which folders and files it's meant to have. Each project ends up a little different.

~/src
$ cp -r booking newthing && cd newthing
$ rm -rf .git deps _build dumps/*
# seeds folder? .env.example? which container name?
# the db container is still called db-booking

That was one morning.

commands
8
looked up again
5
ways it went wrong
4
written down for next time
0

Here's the same morning, with dxthing.

Act II

The dxthing way.

One launcher that lives in one place. Run it in any project and it loads that project's environment and shows the tools that fit it.

01 · One launcher

Every chore, by name.

It sees what the project is, loads its .env, and lists the tools that apply, grouped by what they're for. Nothing to install per project.

~/src/shop
devtools · shop · phoenix · .env loaded
────────────────────────────────────────────────────────────────────────
Data sync ▸ pull_latest_backup Download newest R2 backup → dumps/latest.sql
restore_db Drop local DB, import latest dump, apply migrations
list_backups List backups stored in R2 bucket
Schema ecto_migrate Run pending Ecto migrations
reset_db Wipe and recreate local Docker dev database
Secrets secrets_edit Edit encrypted secrets for this project
Infrastructure check_env Diff .env against .env.example, flag missing vars
────────────────────────────────────────────────────────────────────────
↑↓ choose · tab category · enter run · q quit

02 · Restore

Last night's data, in two keystrokes.

Pull the newest backup, then restore it: the database dropped and reimported, and the migrations applied. The flags live in the tool, not in your head.

~/src/shop
── pull_latest_backup ──────────────────────────────────
dumps/latest.sql · 2026-09-27 03:00 · 184 MB
✓ pull_latest_backup completed.
── restore_db ──────────────────────────────────────────
dropped shop_dev · imported dumps/latest.sql · 14 migrations applied
✓ restore_db completed.

03 · Secrets and .env

Secrets and environment, made human.

Edit a project's secrets without remembering the key file, and see which variables .env is missing before the app tells you at startup.

~/src/shop
── check_env ───────────────────────────────────────
missing from .env: R2_BUCKET, RESEND_API_KEY
✓ check_env completed.
── secrets_edit ────────────────────────────────────
opened secrets/shop.yaml, re-encrypted on save

04 · Every project the same shape

And every project laid out the same.

Next on the roadmap: init gives a project the standard layout, a dt folder for dumps and seeds, and a container name of its own. That's the janitor half.

~/src/newthing
$ devtools init
dt/
├── README.md why this folder is in every project
├── dumps/ production dumps, never committed
│ └── README.md
└── seeds/ sequenced seed files
└── README.md
.env.example every variable the tools expect, named the same everywhere
.devtools container db-shop, so projects never share a port

Act III

Its two jobs.

dxthing is a command hub for everything you'd otherwise look up, and a janitor that keeps every project the same shape.

  1. 1

    It adapts

    It reads the directory it's run in: the stack, the migrations, the .env. The tools that fit show up; the rest stay out of the way.

  2. 2

    A command hub

    Standard scripts for the chores every project has (data, schema, secrets, packages) live in one place, fixed once for every project.

  3. 3

    A janitor

    One .env standard, one folder layout, one container naming rule. The aim is to enforce that shape across the wider system, not just suggest it.

The same chores, both ways
Chore By hand dxthing
Restore production data four commands, looked up two tools
Edit a secret the right env var, eventually secrets_edit
What's missing from .env a diff by hand check_env
A new project a copy of the last one the standard layout

Which command do you look up every time?

Tell me. It's probably the next tool in dxthing.

Get in touch