# Development

## Additional Development Prerequisites

- Docker (you can use [Docker Desktop](https://www.docker.com/products/docker-desktop/)) - To automatically deploy [LocalStack](https://www.localstack.cloud/) which provide a local version of AWS services. Sign up for an account on LocalStack and grab your auth token.
- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) - Configured with separate profiles for local and cloud deployments. LocalStack uses `test` for credentials.
  ```sh
  # ~/.aws/config
  [profile localstack]
  region=us-east-1
  output=json
  endpoint_url = http://localhost:4566

  [profile production]
  region=us-east-1
  output=json
  ```
  ```sh
  # ~/.aws/credentials
  [localstack]
  aws_access_key_id=test
  aws_secret_access_key=test
  ```
- [Caddy](https://caddyserver.com/docs/install) - To proxy localhost to the LocalStack S3 static site. This way the libraries used by atproto auth can have the secure context they require (https or `localhost` domain).
- [Optional] [conda](https://docs.conda.io/projects/conda/en/stable/user-guide/install/index.html) - For python package and environment management. We use a python package for linting CloudFormation templates.
    - Integrate conda in your shell
        ```sh
        conda init
        ```

During development you'll use **local** versions of AWS services to be as close as possible to the way things will run in production. This includes serving the web app as a static site from object storage (an S3 bucket).

Note:  
`make` commands are always run from the `/infra` directory (or with the `-C infra` option).

```sh
make install-cfn-lint   # iff you installed conda for linting CF templates
```

## Workflow

Make sure your shell is not logging these in your command [history](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-HISTCONTROL).
```sh
 export LOCALSTACK_AUTH_TOKEN=your-personal-auth-token
 export IFRAMELY_API_KEY=your-api-key-client-hash
make start-localstack   # once — starts LocalStack in Docker
```

```sh
# terminal 1 (with the exported auth token + API key) do ONE of these:
make local                  # clean deploy infra + build + serve site on :8080
make serve-local            # serve site on :8080

# terminal 2
make run-local-indexer      # run indexer against LocalStack
```
Both will block the terminal. One as the site proxied by Caddy is running; the other as the indexer is running.

If you see `✅ Site synced` followed by some `INFO` messages from Caddy, you're ready to test the app. You can ignore any `WARN` messages from Caddy as they're normal in the dev environment.

**You can now access the app at [http://localhost:8080](http://localhost:8080) or [http://127.0.0.1:8080](http://127.0.0.1:8080).**  

If you only changed the web app or the API Lambda code (no CloudFormation changes) you don't need to redeploy the whole solution. Keep Caddy running from the `make local` command and in another terminal at the project level run:
```sh
pnpm build                              # build the web app
make -C infra sync-site                 # copy it to the S3 bucket

make -C infra create-local-api          # Update CF stack e.g. if you have new Resources or Methods
make -C infra update-local-api          # Update code: build, zip, and redeploy the API Lambda code
```

When you're done testing:
```sh
make -C infra stop-localstack                    # when done
```

### Indexer

LocalStack runs as a Docker container that simulates AWS services. Because it doesn't support real EC2, the indexer EC2 stack is skipped in `make local` — the indexer runs as a plain process instead. **Not idempotent** — running `make run-local-indexer` a second time starts a second instance sharing the same cursor file, causing duplicate event processing.

The `AwsEndpointUrl` CF parameter (default: `http://172.17.0.1:4566` — the Docker bridge gateway) overrides the AWS endpoint so the indexer process reaches LocalStack instead of real AWS when the stack is deployed explicitly with `make deploy-local-indexer`. If your Docker network uses a different gateway, override with:

```sh
LOCALSTACK_ENDPOINT=http://<ip>:4566 make deploy-local-indexer
```

**Opting in to loose documents** — to index `site.standard.document` records whose `site` is a plain URL rather than a publication `at://` URI, add `ACCEPT_LOOSE_DOCUMENTS=true \` to the `run-local-indexer` target in `infra/Makefile`, alongside the other env vars:

```makefile
run-local-indexer: build-indexer
	...
	CURSOR_FILE=/tmp/indexer-cursor \
	ACCEPT_LOOSE_DOCUMENTS=true \
	node indexer/dist/index.js
```

### Backfill

If the code has changed:
```sh
make -C infra update-local-backfill
```

Run it:
```sh
make -C infra run-local-backfill DIDS_FILE=~/dids.txt
```

### Local infrastructure notes

The `deploy-local.sh` script deploys all stacks, uploads the API Lambda code, and writes the correct `VITE_API_URL` and `VITE_API_DID` to `.env.local` before building the front end.
