1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117# Floci Configuration
This directory contains the Floci initialization script and configuration for local AWS service emulation. Floci replaces LocalStack as the dev/CI emulator โ same wire protocol, same port (4566), ~72 MB image, ~26 ms startup, MIT licensed, no auth gates.
Floci upstream: <https://github.com/floci-io/floci>
## Files
- `init-aws.sh` โ Sidecar initialization script that runs once after Floci reports healthy.
- Creates S3 buckets: `equalify-pdf-temp`, `equalify-pdf-results`
- Configures CORS and public-read bucket policies
- Enables versioning on the results bucket
- Sets a 7-day lifecycle policy on the temp bucket
The script is invoked from a companion `floci-init` service in `docker-compose.dev.yml` / `docker-compose.ci.yml` using the `amazon/aws-cli` image, with `AWS_ENDPOINT_URL=http://floci:4566` in the environment so every `aws` command automatically targets Floci.
## Floci services
Floci enables a superset of AWS services by default (s3, monitoring, logs, sqs, sns, lambda, dynamodb, ssm, etc.). The app only uses S3 in dev and CI; the CloudWatch endpoint is kept pointed at Floci as an inert safety rail for a few prod-only code paths that are gated behind `settings.environment == "production"`.
## Accessing Floci
### AWS CLI
Unlike LocalStack, Floci does not ship an `awslocal` wrapper. Use the standard `aws` CLI and point it at Floci via `AWS_ENDPOINT_URL`:
```bash
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
aws s3 ls
aws s3 ls s3://equalify-pdf-temp/
aws s3 cp myfile.pdf s3://equalify-pdf-temp/
aws s3 cp s3://equalify-pdf-temp/myfile.pdf ./
```
Or use the `make floci-debug` helper for a cheat-sheet.
### Python boto3
Configure boto3 to target the Floci endpoint โ identical pattern to LocalStack:
```python
import boto3
s3_client = boto3.client(
"s3",
endpoint_url="http://localhost:4566",
aws_access_key_id="test",
aws_secret_access_key="test",
region_name="us-east-1",
)
print(s3_client.list_buckets()["Buckets"])
```
The app doesn't hardcode any of this โ `boto3` reads `AWS_ENDPOINT_URL_S3` from the environment automatically, so nothing in `src/` knows whether it's talking to Floci or real AWS.
### Direct HTTP API
```bash
# Healthcheck โ root returns 200 with ListAllMyBucketsResult XML
curl http://localhost:4566/
# Access an object (path-style URL, what S3_PUBLIC_URL builds)
curl http://localhost:4566/equalify-pdf-temp/test.txt
```
Floci does not expose a dedicated `/health` endpoint (LocalStack's `/_localstack/health` has no equivalent). The Docker Compose healthcheck uses `GET /` instead, which is cheap and deterministic.
## Persistence
In `docker-compose.dev.yml` Floci runs with `FLOCI_STORAGE_MODE=persistent` and persists to the named volume `equalify-reflow-floci-data`. Buckets and objects survive `docker compose restart floci` and `docker compose down && docker compose up`.
To reset Floci state:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
docker volume rm equalify-reflow-floci-data
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
```
`docker-compose.ci.yml` uses the default in-memory storage mode โ CI doesn't need persistence between runs, and skipping the volume shaves a few hundred milliseconds.
## Troubleshooting
### Buckets not created
The buckets are created by the `floci-init` sidecar, not by Floci itself. Check its logs:
```bash
docker logs equalify-reflow-floci-init
```
You should see `โ Bucket equalify-pdf-temp created successfully` and friends. If the sidecar isn't running, check that Floci reported healthy โ the sidecar's `depends_on` gate blocks it from starting otherwise:
```bash
docker logs equalify-reflow-floci
docker inspect equalify-reflow-floci --format '{{.State.Health.Status}}'
```
### Connection refused
```bash
docker ps | grep floci
curl http://localhost:4566/
```
### Script not executing
Unlike LocalStack's `ready.d` hook, Floci has no built-in init hook โ initialization is handled by the external `floci-init` sidecar. If the sidecar exited before the buckets were created:
1. Check exit code: `docker inspect equalify-reflow-floci-init --format '{{.State.ExitCode}}'`
2. Re-run it manually: `docker compose -f docker-compose.yml -f docker-compose.dev.yml up floci-init`