Skip to content

devbox run hooks for pre, post, and command wrapping #2862

Description

@levonk

What problem are you trying to solve?

I'm trying to wrap every Devbox-executed command with a command wrapper (specifically rtk exec --) for instrumentation, tracing, and policy enforcement without breaking reproducibility.

The core issue is that Devbox currently has no mechanism to intercept or wrap commands executed via devbox run. This makes it impossible to:

  1. Instrument commands: Add tracing/telemetry to every command execution
  2. Enforce policies: Validate or block commands before execution
  3. Sandbox commands: Wrap execution with security/policy tools
  4. Transform commands: Modify arguments, environment, or I/O dynamically

Devbox's execution model is:

devbox run <script> → spawn non-interactive shell → run command

Non-interactive shells always source $BASH_ENV or .zshenv, but this is a fragile workaround, not a proper solution.

What solution would you like?

I propose adding a comprehensive hook system under the existing shell configuration in devbox.json:

{
  "shell": {
    "init_hook": [...],
    "scripts": { ... },
    
    // NEW: Run hooks for devbox run
    "run_hooks": {
      "pre_run": [{
        "command": "policy-checker --cmd {cmd} --args {args}",
        "can_block": true,
        "can_modify_args": false,
        "can_modify_env": false,
        "can_modify_stdin": false
      }],
      "command_wrapper": ["rtk exec --"],
      "post_run": [{
        "command": "telemetry-collector --exit {exit} --stdout {stdout}",
        "can_modify_exit": true,
        "can_modify_stdout": false,
        "can_modify_stderr": false
      }]
    }
  }
}

Three hook types:

  1. pre_run: Runs before command execution

    • can_block: Hook can refuse execution (return non-zero)
    • can_modify_args: Hook can change command arguments
    • can_modify_env: Hook can change environment variables
    • can_modify_stdin: Hook can change stdin input
  2. command_wrapper: Simple string wrapper for the entire command

    • Executes: rtk exec -- <original command>
    • Use case: Simple instrumentation wrappers
  3. post_run: Runs after command execution

    • can_modify_exit: Hook can change the exit code
    • can_modify_stdout: Hook can change stdout
    • can_modify_stderr: Hook can change stderr
    • Receives: exit code, stdout, stderr as context

Key design principles:

  • Explicit capability gates: Each hook declares what it can do (security, clarity, validation)
  • Symmetric design: Both pre and post hooks have consistent gating
  • Default-deny: Dangerous capabilities are opt-in
  • Follows Devbox conventions: Uses existing shell namespace
  • Backward compatible: All hooks are optional

Additional context:

This feature would enable:

  • Security tools: Policy enforcement, sandboxing, secret scanning
  • Observability: Telemetry, tracing, execution logging
  • Development workflow: Input validation, output formatting, conditional execution
  • Tool integration: OpenTelemetry, Hermes, policy agents, sandbox wrappers

The implementation would be straightforward (wrap the exec call in the devbox run codepath) and high-value for the ecosystem without breaking reproducibility when properly configured.

Alternatives you've considered

  1. Devbox shell hooks → Only run for devbox shell, not devbox run
  2. Devbox scripts → Requires wrapping each script manually (tedious, error-prone)
  3. PATH shims → Devbox resolves commands inside the environment, bypassing wrappers
  4. Aliasing → Aliases don't apply in non-interactive shells
  5. $BASH_ENV/.zshenv workaround → Fragile, doesn't provide proper control flow or access to execution context
  6. post_run hooks only → Can't prevent execution or modify inputs

This design matches patterns from other tools:

  • Nix flakes: apps.<name>.program wrapping
  • direnv: Hook mechanisms for environment setup
  • Bazel: Toolchain wrapping and hooks
  • Git: pre-commit/post-commit hooks
  • CI/CD systems: before_script/after_script stages

Activity

  1. mikeland73 commented on Sep 28, 2026

    @mikeland73
    Collaborator

    Triage: looks hard, p4

    • This is a feature request, not a bug. On current main, devbox run has no pre, post or wrapper hook. Scripts and commands go through Devbox.RunScript (internal/devbox/devbox.go:273), and nothing there intercepts them.
    • The proposal covers a lot: gated pre and post hooks that can change args, env, stdin, stdout, stderr and the exit code. The companion PR Add hook system for devbox run commands (#2862) #2863 adds about 2.7k lines across 18 files. Capturing and rewriting stdout/stderr also changes how interactive and TTY commands behave. It adds a new security-sensitive config surface as well, so it needs a design decision from maintainers.
    • Suggested direction: a small first step would be only command_wrapper, a prefix argv applied in RunScript, which is easy to implement. The rest of the pre/post capability model is the part that looks hard to maintain. Workarounds for now are a wrapper script defined as a devbox script, or BASH_ENV, as you noted.

    Generated by Claude Code

  2. levonk commented on Sep 30, 2026

    @levonk
    Author

    @mikeland73 Would you like me to submit a issue and PR just for command wrapper?

  3. mikeland73 commented on Sep 30, 2026

    @mikeland73
    Collaborator

    @levonk hold off for now. I'm mostly going through bugs but don't have bandwidth to address new features.

    This feature is one that we've always wanted so really appreciate the contribution. I want to spend some time making sure the schema makes sense and is extensible (e.g. what if we want to add more hooks in the future, not just run hooks).

    Also, orthogonally, I want to get rid of "shell" field in devbox.json and just flatten it. There's a PR for that, but I haven't had the time to test and merge it.

  4. levonk commented on Oct 1, 2026

    @levonk
    Author

    We really appreciate all the work. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions