Design and Philosophy

This document describes the core design principles, mental model, and non-goals of envstack.

It is intended to explain why envstack behaves the way it does, not to document every feature or command-line option.

Design goals

envstack is designed to make environment configuration:

envstack assumes that real-world environments are hierarchical and that configuration should reflect that structure.

Mental model

envstack treats environment configuration as a stack.

Each layer contributes variables, defaults, or overrides. Layers are applied in a defined order, and precedence is explicit.

You can think of envstack as:

Environment activation + configuration layering

or:

Policy-driven composition of environment variables

envstack does not attempt to infer intent or solve dependency graphs. All composition is declared explicitly.

The shortest useful rule is:

Files identify stacks; directories identify scope; ENVPATH defines precedence.

For example, these files all describe the same stack in different scopes:

/studio/prod/env/mytool.env
/studio/dev/env/mytool.env
/studio/project/foo/env/mytool.env

mytool.env identifies the tool or stack being configured. The containing directory identifies the scope in which that definition applies.

Environment stacks

An environment stack is an ordered collection of environment definitions.

Stacks may be composed by:

Variables flow through the stack according to precedence rules.

flowchart TD
  base[base.env]
  prod[prod.env]
  project[project.env]
  task[task.env]
  keys[keys.env]

  base --> prod --> project --> task
  keys -. include .-> prod
  keys -. include .-> project

Precedence and overrides

Precedence in envstack is explicit and ordered.

There is no implicit merging or magic behavior. If a value changes, it is because a later layer overrides it.

This makes environment behavior predictable and debuggable.

Hierarchy and inheritance

envstack environments are typically arranged hierarchically:

Downstream environments inherit upstream configuration and apply targeted overrides. This avoids duplication while preserving clarity.

ENVPATH

envstack discovers environment definitions using the ENVPATH environment variable.

ENVPATH is an ordered, colon-separated list of directories that envstack searches when resolving environment names.

Earlier ENVPATH entries win when the same env name exists in multiple locations:

ENVPATH=/mnt/tools/dev/env:/mnt/tools/prod/env

Resolution follows these rules:

ENVPATH is intentionally simple and filesystem-backed. It allows:

ENVPATH plays the same role for envstack that PATH plays for executables.

For example:

export ENVPATH=/studio/project/foo/env:/studio/prod/env
envstack mytool

In that case envstack resolves mytool.env from both directories, but the project-scoped definition has higher precedence because its directory appears first in ENVPATH.

Configuration ownership and revision control

envstack deliberately separates configuration composition from source control and deployment.

Recommended practice:

Environment definitions should be revision-controlled with the system or scope that owns them. Their deployed filesystem location defines scope, while ENVPATH defines precedence.

That usually means there is not one repository containing every version of a stack such as mytool.env.

An application repository may own its canonical definition:

mytool.git/
    env/
        mytool.env

A facility or site configuration repository may own organization-wide policy:

studio-config.git/
    env/
        mytool.env
        maya.env

After deployment, those files might live at:

/studio/prod/env/mytool.env

A project configuration repository can then add project-specific overrides:

foo-config.git/
    env/
        mytool.env

deployed to:

/studio/project/foo/env/mytool.env

At runtime:

export ENVPATH=/studio/project/foo/env:/studio/prod/env
envstack mytool

The project-scoped definition overrides the facility-scoped definition without requiring either repository to own the other’s history.

Stack and scope are different dimensions

Names such as mytool and scopes such as prod, dev, or project/foo represent different concerns.

                  stack
                   |
/studio/prod/env/mytool.env
        |
       scope

envstack does not prescribe a fixed hierarchy. What matters is that scopes are represented by directories and combined explicitly through ENVPATH.

Avoid environment branches as the primary model

As a general operating model, it is usually better not to represent runtime scopes primarily as Git branches such as prod, dev, or project-foo.

That pattern tends to couple application revision and runtime configuration too tightly, makes it harder to run the same application revision under different contexts, and blurs ownership of configuration history.

This is a recommendation, not a prohibition. envstack does not require any particular repository layout.

Relationship to deployment tools

envstack is not a Git client, package manager, or deployment system.

Revision-controlled environment definitions still need to be published to the filesystem locations referenced by ENVPATH. That publication can happen manually, through CI/CD, via configuration management, or through a deployment tool such as distman.

Conceptually:

Git repository
    |
    v
deployment or distribution system
    |
    v
scoped env directories
    |
    v
ENVPATH ordering
    |
    v
envstack activation

Includes

Environment definitions may include other environments explicitly.

Includes allow:

Includes are declarative and resolved as part of the stack.

Variable resolution

Variables may:

Resolution follows shell-like semantics and is performed explicitly.

envstack allows:

This makes configuration errors visible early.

What envstack does not do (non-goals)

envstack intentionally does not:

Those concerns are left to other tools or to policy defined by the user.

envstack focuses solely on composition and activation of configuration.

Composability with other systems

envstack is designed to compose cleanly with other systems.

It can be layered on top of:

envstack does not require a specific packaging or deployment model. It assumes only that environments can be described declaratively.

Early binding vs late binding environments

envstack uses a late-binding environment model.

Environments are resolved at activation time, not at install time, build time, or package-resolution time.

This means:

This model contrasts with:

envstack treats environments as live configuration, composed explicitly each time they are activated.

Late binding is a deliberate design choice. It favors:

envstack provides policy-defined isolation rather than runtime-enforced isolation, relying on explicit configuration and directory layout.

Opinionated by design

envstack is intentionally opinionated:

These constraints exist to keep complex environments understandable at scale.

Summary

envstack exists to make hierarchical environment configuration:

It embraces complexity where it exists, rather than hiding it behind implicit behavior.