# Installation

> Install: Required tools, Nix option, encrypted environment setup, Docker resources, LocalStack, FusionAuth, and first setup command.

- Repository: macro-inc/macro
- GitHub: https://github.com/macro-inc/macro
- Human docs: https://grok-wiki.com/public/docs/macro-inc-macro-bb988e1a448e
- Complete Markdown: https://grok-wiki.com/public/docs/macro-inc-macro-bb988e1a448e/llms-full.txt

## Source Files

- `RUNNING_LOCALLY.md`
- `flake.nix`
- `justfile`
- `local_stack.just`
- `docker-compose-databases.yml`
- `rust/cloud-storage/README.md`

---

---
title: "Installation"
description: "Install: Required tools, Nix option, encrypted environment setup, Docker resources, LocalStack, FusionAuth, and first setup command."
---

Macro local installation is driven by `just setup` at the repository root. The setup path decrypts `.env`, creates shared Docker networks and volumes, provisions LocalStack AWS resources, initializes the local Postgres database, configures a local FusionAuth stack, and builds local Rust service images.

## Prerequisites

| Tool | Required for |
|---|---|
| `just` | Repository task runner |
| Docker with Compose v2 | Databases, services, FusionAuth, LocalStack |
| `sops` | Decrypting `.env-local*.enc` files |
| AWS CLI and AWS credentials | LocalStack provisioning and encrypted environment access |
| `pulumi` | Local FusionAuth stack configuration |
| `bun` | Frontend and FusionAuth stack dependencies |
| Node.js | JavaScript tooling and Pulumi Node runtime |
| `sqlx-cli` | Database create, migrate, prepare, and reset commands |
| Rust toolchain | Backend builds and local Rust tests |

<Note>
The repository currently documents local development as work in progress and primarily supports running services against dev assets or the provided local E2E stack.
</Note>

## Nix option

If Nix is available, use the repository flake instead of installing most tools manually:

```bash
nix develop
```

The default shell provides the Rust toolchain, `just`, `bun`, `pulumi`, `sops`, `sqlx-cli`, Node.js 24, Docker Compose tooling, `jq`, and related backend/frontend utilities. It also sets `SOPS_KMS_ARN` for encrypted environment decryption.

For Tauri/frontend platform work, use the JavaScript app shell:

```bash
nix develop .#js-app
```

<Warning>
Nix supplies CLI tools, not the Docker daemon. Docker must still be running on the host.
</Warning>

## Encrypted environment setup

The root `justfile` decrypts encrypted dotenv bundles into a plain root `.env` file.

| Command | Input | Output | Notes |
|---|---|---|---|
| `just get_environment` | `.env-local.enc` | `.env` | Fully local backing services |
| `just get_environment dev` | `.env-localdev.enc` | `.env` | Dev backing services, used for ad-hoc dev-service work |
| `just edit_environment` | `.env-local.enc` | encrypted file edited through `sops` | Maintainer workflow |
| `just edit_environment dev` | `.env-localdev.enc` | encrypted file edited through `sops` | Maintainer workflow |
| `just fix_environment [dev]` | encrypted dotenv | re-encrypted dotenv | Decrypts with `--ignore-mac`, re-encrypts, removes temporary plaintext |

If you are not inside `nix develop`, export the KMS ARNs before decrypting:

```bash
export SOPS_KMS_ARN="arn:aws:kms:us-east-1:569036502058:key/mrk-cab29bf948044eb79005a81f48d40e93,arn:aws:kms:us-west-1:569036502058:key/mrk-cab29bf948044eb79005a81f48d40e93"
```

When `just get_environment dev` is used and `~/.aws/credentials` exists, the recipe replaces `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in `.env` from the `[default]` profile.

<Warning>
`just setup` always runs `just get_environment` with no suffix, so it regenerates `.env` from `.env-local.enc`.
</Warning>

## First setup command

Run from the repository root:

```bash
just setup
```

`just setup` performs this sequence:

<Steps>
  <Step title="Decrypt the root environment">
    Generates `.env` from `.env-local.enc`.
  </Step>
  <Step title="Create shared Docker resources">
    Creates the `databases` and `auth` networks plus the shared local volumes.
  </Step>
  <Step title="Provision LocalStack">
    Starts the `localstack` container on port `4566`, then creates local SQS queues, DynamoDB tables, S3 buckets, CORS rules, and the document upload finalizer notification.
  </Step>
  <Step title="Initialize local databases">
    Starts Postgres and Redis, creates `macrodb`, runs migrations through SQLx, then stops the database Compose file.
  </Step>
  <Step title="Configure FusionAuth">
    Starts the local FusionAuth stack, waits for health, initializes the Pulumi `local` stack, patches root `.env` with local FusionAuth values, and stops FusionAuth.
  </Step>
  <Step title="Build service images">
    Builds `macro-local-rust-services:dev` and the local search processing image.
  </Step>
</Steps>

Expected final output:

```text
Setup complete.
```

## Docker resources

Local Docker resources are intentionally frozen to the `macro` Compose project. Multiple checkouts and worktrees share the same containers, volumes, networks, LocalStack instance, and FusionAuth instance.

<Warning>
Do not run two local stacks at the same time from different checkouts.
</Warning>

### Shared networks and volumes

`just create_networks` creates:

| Resource | Name |
|---|---|
| Network | `databases` |
| Network | `auth` |
| Volume | `macro_postgres_data` |
| Volume | `macro_redis_data` |
| Volume | `macro_opensearch_data` |
| Volume | `fusionauth_db_data` |
| Volume | `fusionauth_config` |

### Database Compose services

`docker-compose-databases.yml` defines:

| Service | Image | Ports | Purpose |
|---|---|---:|---|
| `postgres` | `pgvector/pgvector:pg16` | `5432` | Local `macrodb` database |
| `redis` | `redis/redis-stack:latest` | `6379`, `8001` | Redis plus Redis Stack UI/tools |
| `search` | `opensearchproject/opensearch:latest` | `9200`, `9600` | Local OpenSearch |

The local database URL used by cloud-storage justfiles is:

```text
postgres://user:password@localhost:5432/macrodb
```

## LocalStack

Local AWS emulation is handled by `local_stack.just`.

```bash
just setup_localstack
```

The setup starts `localstack/localstack:4` with:

| Setting | Value |
|---|---|
| Container name | `localstack` |
| Network | `databases` |
| Port | `4566` |
| Services | `sqs,dynamodb,s3` |
| Health endpoint | `http://localhost:4566/_localstack/health` |

The recipe provisions SQS queues used by notification, email, contacts, conversion, document deletion, document upload finalization, document text extraction, search events, and static-file events. It also creates these DynamoDB tables:

| Table | Key shape |
|---|---|
| `bulk-upload` | `PK` + `SK`, with `DocumentPkIndex` |
| `connection-gateway-table` | `PK` + `SK`, with `ConnectionPkIndex` |
| `static-file-metadata` | `file_id` hash key |

S3 buckets created locally:

| Bucket |
|---|
| `macro-email-attachments` |
| `doc-storage` |
| `docx-upload` |
| `static-file-storage` |
| `bulk-upload-staging` |

All buckets receive CORS rules for `http://localhost:3000` through `http://localhost:3009`. The `doc-storage` bucket is also wired so `s3:ObjectCreated:*` events send messages to `document-upload-finalizer-queue`.

Backend AWS clients switch to LocalStack when `LOCAL_AWS_URL` is set. In that mode, the Rust AWS config uses `us-east-1`, test credentials, the configured endpoint URL, and path-style S3 behavior.

## FusionAuth

The local FusionAuth stack lives under `infra/stacks/fusionauth-instance`.

```bash
just setup_fusionauth
```

The root setup command calls the same stack setup through:

```bash
just infra/stacks/fusionauth-instance/setup
```

Local FusionAuth uses:

| Setting | Value |
|---|---|
| App image | `fusionauth/fusionauth-app:1.62.1` |
| Database image | `postgres:16.0-bookworm` |
| Port | `9011` |
| Runtime mode | `development` |
| App URL inside Docker | `http://fusionauth:9011` |
| Local issuer | `local.macro.com` |
| Pulumi stack | `local` |

Local admin credentials documented by the stack:

```text
username: admin@macro.com
password: macroIsGreat!
api-key: bf69486b-4733-4954-a44e-2e1b5f2c8a91
```

<Warning>
Create the local Pulumi stack as `local`; do not use a `macro-inc/` stack prefix for the local FusionAuth instance.
</Warning>

During setup, the FusionAuth recipe patches root `.env` with local values including:

| Key |
|---|
| `AUDIENCE` |
| `FUSIONAUTH_TENANT_ID` |
| `FUSIONAUTH_CLIENT_ID` |
| `FUSIONAUTH_CLIENT_SECRET_KEY` |
| `JWT_SECRET_KEY` |
| `ISSUER` |
| `FUSIONAUTH_BASE_URL` |
| `FUSIONAUTH_API_KEY_SECRET_KEY` |
| `FUSIONAUTH_OAUTH_REDIRECT_URI` |

`just run_local` also calls `just patch_local_fusionauth_env`, which patches `.env` again if the local Pulumi stack exists. If FusionAuth is not running, that recipe starts it temporarily to read the OAuth client secret, then stops it.

## Running after installation

Start backend services:

```bash
just run_local
```

If service images changed, rebuild selected images while starting:

```bash
just run_local --build
```

By default, `convert_service` and `search_processing_service` are not required for the frontend dev path. `search_processing_service` is behind the `processors` Compose profile and is pinned to `linux/amd64` because its local PDFium library is amd64-only.

Start the frontend against local services:

```bash
cd js/app
bun i
just local
```

## Local E2E smoke setup

After installation:

```bash
just local-e2e
```

This uses `docker-compose.local-e2e.yml` overrides so services use local Postgres and LocalStack instead of shared dev assets, seeds deterministic fixture data, launches the frontend with local bearer-token auth, and runs Playwright.

Additional E2E commands:

```bash
just local-e2e-rust
just local-e2e-all
just local-e2e-ui
```

The local E2E seed path is guarded: it requires `LOCAL_E2E_SEED=true` and rejects database URLs that are not the local Docker database shape `postgres://user:...@(localhost|127.0.0.1|postgres):5432/macrodb`.

## Cleanup and reset

| Command | Effect |
|---|---|
| `just stop-local` | `docker compose down` for local services |
| `just stop-databases` | Stops the database Compose file |
| `just stop_fusionauth` | Stops the FusionAuth Compose file |
| `just destroy` | Destroys the local FusionAuth stack and runs `docker compose down -v` |
| `just docker_cache_clear` | Clears all BuildKit cache |
| `just docker_cache_clear_targets` | Clears Rust target cache mounts only |
| `just docker_cache_usage` | Shows BuildKit cache usage |

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| `.env not found` during local run | Environment was not decrypted | Run `just get_environment` or rerun `just setup` |
| `Pulumi local stack not found` | FusionAuth local stack has not been initialized | Run `just setup` |
| LocalStack AWS commands fail | AWS CLI is missing or unavailable | Install/configure AWS CLI; LocalStack recipes call `aws --endpoint-url=http://localhost:4566 ...` |
| Browser cannot resolve LocalStack bucket hostnames | Presigned URLs generated inside Docker use `localstack` hostnames | Use local mode with `LOCAL_AWS_URL`; the Rust AWS helper rewrites local URLs to `localhost` |
| Different checkout changes local containers | Docker resources are shared under Compose project `macro` | Stop the other checkout before starting this one |
| FusionAuth client secret cannot be read | FusionAuth is not running or local stack is incomplete | Run `just setup_fusionauth` or rerun `just setup` |

## Related pages

<CardGroup>
  <Card title="Running locally" href="/running-locally">
    Backend, frontend, and local E2E commands after installation.
  </Card>
  <Card title="Cloud storage" href="/cloud-storage">
    Rust backend services, database setup, and deployment notes.
  </Card>
  <Card title="FusionAuth instance" href="/fusionauth-instance">
    Local FusionAuth stack behavior, Pulumi configuration, and credentials.
  </Card>
</CardGroup>
