Skip to content

Support .beads/.env for per-project Dolt credentials #2520

@restarter

Description

@restarter

Problem

There's no per-project way to configure Dolt credentials. This creates a dilemma when you have a mix of local and remote projects:

project-a/  → remote Dolt on VPS (database "app_a")
project-b/  → remote Dolt on VPS (database "app_b")
project-c/  → local embedded Dolt, no remote server at all

If I set BEADS_DOLT_* env vars globally (e.g., in .zshrc), then project-c breaks - it tries to connect to the remote server where no database exists for it, instead of using local embedded Dolt.

If I don't set them globally, then project-a and project-b can't connect to the remote server without external tooling.

Why external tooling doesn't work

direnv / .zshrc functions - only work in interactive terminals. Tools like Claude Code execute each shell command in a separate non-interactive subprocess (/bin/sh -c "..."). The direnv hook (eval "$(direnv hook zsh)") never fires, and .zshrc functions aren't loaded. Same applies to any AI coding agent, CI/CD pipeline, or script that spawns bd as a subprocess.

Shell wrapper script (current workaround) - a bd script in PATH that finds .beads/.env, exports vars, and calls the real binary. Works everywhere but is fragile: breaks on go install updates (overwrites the wrapper), requires manual setup per machine, and adds an indirection layer.

metadata.json - supports dolt_server_host/dolt_server_user but dolt_server_port is deprecated, and password is intentionally env-only (configfile.go:34). Credentials end up split across two config sources.

The common thread: the solution needs to live inside bd itself, not in the shell environment.

Proposed Solution

Auto-load .beads/.env into process environment before any GetDolt*() calls.

Implementation

In PersistentPreRun (cmd/bd/main.go), after beadsDir is resolved (~line 476) but before configfile.Load() (~line 492):

envFile := filepath.Join(beadsDir, ".env")
if _, err := os.Stat(envFile); err == nil {
    _ = gotenv.Load(envFile)
}

gotenv (github.com/subosio/gotenv) is already an indirect dependency via viper. gotenv.Load() is non-overriding - shell env always wins.

.beads/.env format

BEADS_DOLT_SERVER_HOST=<server-ip>
BEADS_DOLT_SERVER_PORT=3306
BEADS_DOLT_SERVER_USER=<username>
BEADS_DOLT_PASSWORD=<password>

How it solves the problem

  • project-a/.beads/.env exists → connects to remote server
  • project-b/.beads/.env exists → connects to remote server
  • project-c/ has no .beads/.env → uses local embedded Dolt (current default, unchanged)

No global env vars needed. No external tools. No shell wrappers. Works in terminals, AI agents, CI/CD, scripts - anywhere bd runs.

Env var precedence (preserved)

shell environment > .beads/.env > metadata.json defaults

Scope limitation

BEADS_DIR in .beads/.env would NOT affect config.yaml discovery (loaded earlier in init()config.Initialize()). This is acceptable - the primary use case is BEADS_DOLT_* connection params.

Environment

  • beads v0.59.0 (dev)
  • macOS, multiple projects with mix of local and remote Dolt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions