!Warning! this is not meant for production since the goal of this project is remote code execution. For learning purposes only.
Configure and monitor the remote execution of remote shell commands with the abilty to pass secrets to them. Secrets are encrypted at rest.
- Elixir
- Usage Rules
- Phoenix
- LiveView for admin UI
- Ash: Define your model, derive the rest -- including paginated remote queries and many other table stakes
- Ash Postgres for persistence
- Ash Authentication
- Oban for robust job execution
- ReactJS
- TanStack Start
- TanStack Query
- shadcn/ui for UI components
- Embeds Oban Web via an IFrame
- Communicates with Phoenix/Ash via Ash TypeScript. As far as the app is concerned, it's TypeScript All the Way Down!
The React application lets users:
- Define commands (shell scripts) to run with constraints (currently only maximum execution time). Secrets can be emedded in commands by referring to them by name, such as
$secret_name. - Define execution environments
- Manage encrypted secrets that can be embedded into commands securely
- They don't appear, for example, in history files or
ps - Secrets can have one value per environment
- They don't appear, for example, in history files or
- Schedule commands to run on specified environments using crontab expressions
- Monitor command execution events with filters and pagination
- View additional details via Oban Web
brew install postgresql@18
brew install elixirVerify:
elixir --version
mix --versioncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bashReload your shell:
source ~/.nvm/nvm.shgit clone https://github.com/dev-guy/CommandStudio.git
cd CommandStudiocd webapp/studio
nvm install
nvm use
node -vBackend:
cd phoenix
mix deps.getFrontend:
cd webapp/studio
npm installcd phoenix
mix setupThe admin user is admin@example.com
cd phoenix
mix run priv/repo/seeds.exsThis command outputs a random password. Take a note of it.
Start Phoenix:
cd phoenix
mix phx.serverRun the React UI:
cd webapp/studio
npm run devGo to:
- The following MCP servers are recommended:
- "boot" your AI Assistant by instructing it to "follow the instructions in ai/boot.md"
- The model has many to many relationships. These are harder than you might think.
- Contains nontrivial searches with pagination
- The AI assistant added password-based authentication via Ash Authentication plus username/password form + a logout menu in about 10 minutes. The most time I spent was deciding how I wanted the admin account to be seeded.
The Command resource defines Ash generic actions (action :enqueue_run, :enqueue_run_in, :enqueue_run_force) that call Jobs.enqueue_command/2, and that function enqueues the Oban job via Oban.insert/1.
See the Mermaid sequence document: docs/ash-oban-sequence.md
Why use Oban? Oban is robust and has a fantastic operational UI. It has the following benefits that took years to perfect:
- Durability: jobs are persisted in Postgres, so work survives restarts
- Reliability: retries, backoff, and failure tracking are built in
- Observability: Oban Web provides real-time queue and job introspection
- Control: queue-level tuning and worker isolation keep execution predictable
- It works with Ecto/Postgres
The following documents were generated with ChatGPT 5.2 and then fed into Codex 5.3 Medium:
Development did not start until AGENTS.md was generated via Usage_Rules.
- TanStack Start was initialized by Codex. However, manual installation is recommended.
- I didn't specify using
class-variance-authority. Models already know about it. webapp/studio/eslint.config.jswas configured withglobalIgnores(['dist', 'src/lib/ash_rpc.ts'])so ESLint skips the generated Ash RPC client file. Otherwise, eslint will report many errors.

