# The folder contract

URL: https://mechcrate.dev/docs/start/folder-contract/
Source: site/apps/site/src/content/docs/docs/start/folder-contract.mdx

---

import FolderContract from '@/components/diagrams/FolderContract.astro';

Every mx project has the same shape. That is the whole mechanism behind "one
`make dev` works everywhere, one router serves everything, one MCP server
understands any project".

<FolderContract />

## The tree

```
project-root/
├── Makefile                 # the CLI: make dev / up / down / logs / sh ...
├── make/                    # make modules (common.mk, dev.mk, ...)
├── scripts/                 # shell scripts the make targets call
├── apps/                    # application source code
│   └── <service>/
└── docker/
    ├── .config/             # env files: .env.shared, .env.secrets, .env.<service>
    ├── compose/             # atomic per-service compose: <service>.yml + <service>.dev.yml
    ├── system/              # 1:1 host-to-container mounts
    └── dockerfiles/         # one directory per service
```

A project with `--infra cloudflare` also carries `infra/cloudflare/` and
`make/cloudflare.mk`; see [Cloudflare deploy](/docs/framework/cloudflare-deploy/).

## Who owns what

The split is not a style preference. `mx upgrade` reads it directly, and it
decides whether a file can be replaced under you.

| Path | Owner | Upgrade behaviour |
|---|---|---|
| `Makefile`, `make/*.mk`, `scripts/*.sh`, `scripts/*.mjs` | mx | **Tooling**: upgrade offers to update these |
| `docker/compose/`, `docker/dockerfiles/`, `docker/system/` | you | **Config**: added if missing, never overwritten |
| `apps/<service>/` | you | untouched |
| `docker/.config/` | you | untouched; `.env.secrets` is gitignored |
| `infra/cloudflare/`, `make/cloudflare.mk`, `scripts/cf-*.sh` | mx | **Conditional**: only if `infra/cloudflare/` exists |

The practical reading: **your code and your compose files are yours.** The verb
layer belongs to the framework, which is what lets improvements to `make dev`
reach projects that were scaffolded a year ago. (`mx upgrade` is the command that
does this, and the categories in this table are the ones it honours. See
[Upgrade](/docs/framework/upgrade/).)

## Why each directory exists

**`Makefile` + `make/`** are the verbs. `make/` is one module per verb group
(`dev.mk`, `logs.mk`, `build.mk`, …), auto-included by a wildcard in the
`Makefile`. Adding a module is how a feature like Cloudflare adds targets without
touching the root file.

**`scripts/`** holds the implementations. Make targets stay one line; the logic
lives in shell scripts that are readable, greppable and runnable on their own.

**`apps/<service>/`** is one directory per service, named exactly as the service.
Compose, dockerfile and env file all key off that same name, so there is never a
mapping table to consult.

**`docker/compose/`** is one baseline file per service plus a `.dev.yml` override.
This is what makes the *subset* the unit of work: `make dev s=api` composes only
the files that service needs. See
[Compose &amp; env conventions](/docs/framework/compose-env/).

**`docker/.config/`** has three env layers: `.env.shared` for everything,
`.env.secrets` (gitignored, generated from `.env.secrets.template` by
`make init`) for credentials, `.env.<service>` for per-service settings.

**`docker/system/`** holds 1:1 host-to-container mounts. If a service needs a
config file, a seed dataset or a persisted directory, it lives here at the same
relative path the container sees.

**`docker/dockerfiles/<service>/`** carries `app` for development and `app.prod`
for the shipped image. Multi-stage targets (`development`, `production`) let the
same file serve both.

## Nesting

A project root is self-contained, which means an mx project can live inside
another repository. This site is exactly that: `site/` in the mech-crate
repository is a complete mx project with its own `Makefile`, `make/`, `scripts/`
and `docker/` tree, and the Astro app under `site/apps/site/`.

## Next

- [CLI reference](/docs/start/cli-reference/): the verbs the contract enables
- [Compose &amp; env conventions](/docs/framework/compose-env/): the `docker/` half in detail
