syncstorage-rs Agent Skills & AI Rules

GitHub

Sync Storage server in Rust

1,823 stars Rust Rust 1 file ยท ~110,478 tokens #rust#services-engineering-team#spanner#sync
RAW Rules
34 Discovered Rules โ€ข ~59,069 Estimated Tokens
Quick:
Loading skill content...

Claude Agent Guidelines & System Prompt

CLAUDE.md claude-rule ~2,800 tokens
Raw
# syncstorage-rs

Firefox Sync storage and token server, written in Rust. The workspace contains a
Rust multi-crate server plus Python utilities and integration/end-to-end tests.

## Agent rules

Security takes absolute precedence. This repository handles synchronization of
user data and token management to access storage nodes.

### 1) Scope & writes

- Operate strictly within this repo root; normalize paths; do not follow
  symlinks outside the repo.
- Writes are allowed to the working tree. Always present a diff for review
  before any staging or commit.
- Do not modify files adjacent to a requested change. Mention issues found
  but do not fix them unless asked.
- Ask before running any command (build, test, install, DB ops, git,
  service management). Do not run `git add`, `git commit`, `git push`, or
  `git rebase` unless explicitly instructed.

### 2) Non-negotiables

- **Secrets:** never read, print, summarize, or transmit secret files or
  values. Use placeholders (`YOUR_API_KEY_HERE`) in examples.
- **External network:** only with explicit approval to a trusted, documented
  endpoint.
- **Pipelines & contracts:** flag breaking API or contract changes. Do not
  alter GitHub Actions defined in `.github/`, any configuration for CI/CD workflows or git hooks without explicit, reviewed
  justification.
- **Published DB migrations:** never edit existing published migration files or schema definitions (ex. `syncstorage-spanner/src/schema.ddl`), whether for MySQL, Postgres, or Spanner.
  Always add a new forward migration and a separate rollback migration.
- **Workspace recommendations:** the repo deliberately ships no
  `.vscode/extensions.json`. Do not re-add one. Useful extensions are listed
  in `.vscode/README.md` for contributors to install at their own discretion.

### 3) Do-not-touch paths

Never read or write these paths. This list mirrors `.gitignore` โ€” if there is
a discrepancy, `.gitignore` is the source of truth.

```
service-account.json      # GCP credentials
.sentryclirc              # Sentry auth token
.envrc                    # direnv secrets
config/local.toml         # local server config (may contain secrets)
tools/tokenserver/loadtests/*.pem
tools/tokenserver/loadtests/*.pub
target/                   # Rust build artifacts
*.xml                     # test result files
openapi.json              # generated artifact
book/                     # generated docs
docs/output/              # generated docs
venv/                     # Python virtual environments
.install.stamp            # Poetry install marker
workspace/                # CI artifact directory
```

### 4) Git commit messages

Follow the commit message format defined in [CONTRIBUTING.md](CONTRIBUTING.md) Git Commit Guidelines.

- Format: `type(scope): subject` โ€” `type` is one of `feat`, `fix`, `docs`,
  `style`, `refactor`, `perf`, `test`, `chore`
- Subject: imperative present tense, no capital first letter, no trailing period
- Body: explain motivation and contrast with previous behavior
- Footer: `Closes STOR-1234` (Mozilla engineers โ€” matches the Jira ticket) or
  `Closes #N` / `Issue #N` (community contributors โ€” matches the GitHub issue number);
  `BREAKING CHANGE:` for breaking changes
- Branch naming (Mozilla engineers): `type/description-STOR-1234` where `1234` must match the numeric suffix of the associated Jira ticket
- Branch naming (community): `type/description-1112`
- All commits must be GPG/SSH signed (enforced by CI)

Example:

```
feat: add node reassignment on capacity overflow

Nodes at 100% capacity previously returned 503 rather than
assigning the user to an available node.

Closes #42.
```

## Project layout

```
syncserver/            Main Actix-web server (entry point)
syncstorage-db/        Syncstorage database abstraction layer
syncstorage-db-common/ Syncstorage shared database types and traits
syncstorage-mysql/     MySQL backend for syncstorage
syncstorage-postgres/  PostgreSQL backend for syncstorage
syncstorage-spanner/   Google Cloud Spanner backend for syncstorage
syncstorage-settings/  Syncstorage configuration types
tokenserver-db/        Tokenserver database abstraction layer
tokenserver-db-common/ Tokenserver shared database types
tokenserver-mysql/     MySQL backend for tokenserver
tokenserver-postgres/  PostgreSQL backend for tokenserver
tokenserver-auth/      HAWK token generation and verification
tokenserver-common/    Shared tokenserver utilities
tokenserver-settings/  Tokenserver configuration types
syncserver-common/     Shared server utilities
syncserver-db-common/  Shared database utilities
syncserver-settings/   Syncserver configuration types
glean/                 Glean telemetry crate
tools/
  integration_tests/   Pytest-based integration + e2e tests
  tokenserver/         Tokenserver utilities and load tests
  spanner/             Spanner utilities
  hawk/                HAWK token generation utility
  syncstorage-loadtest/ Molotov load tests
docker/                Docker Compose files for each backend
config/                Local TOML configuration files
docs/                  mdBook documentation source
.github/               GitHub CI Actions and configuration
```

## Rust builds

The default feature set targets MySQL, however production uses the Spanner database with MySQL Tokenserver. Backend features are mutually exclusive.

```bash
# MySQL (default)
cargo build

# PostgreSQL
cargo build --no-default-features --features=syncstorage-db/postgres --features=tokenserver-db/postgres --features=py_verifier

# Spanner
cargo build --no-default-features --features=syncstorage-db/spanner --features=py_verifier
```

## Rust tests

Tests require `RUST_TEST_THREADS=1` (many tests share a real database).

```bash
# MySQL unit tests (default DATABASE_URL envs set in Makefile)
make test

# Specific package
make test ARGS="--package syncstorage-db"

# With coverage
make test_with_coverage          # MySQL
make postgres_test_with_coverage
make spanner_test_with_coverage
```

## Clippy / formatting / audit

Formatting checks are necessary before applying any code changes, as they
can capture problematic patterns or compiler issues in advance.

```bash
cargo fmt -- --check             # format check
cargo fmt                        # auto-format

make clippy_mysql
make clippy_postgres
make clippy_spanner

make clippy_release_mysql        # release-mode (catches dead code etc.)
make clippy_release_postgres
make clippy_release_spanner

cargo audit                      # check dependencies for known CVEs (run by CI on every push)
```

After editing Rust code, run clippy and the unit tests for the affected backend before finishing the task; the `rust-code-smell` skill covers changed files across backends.

## Python tooling

All Python tooling uses [Poetry](https://python-poetry.org/). The root
`pyproject.toml` covers the `tools/` tree. Each subdirectory under `tools/` has
its own `pyproject.toml` for its specific dependencies.

All of these Python checks must be run with any related change to a Python
utility or test.

```bash
make install                     # install root dependencies
make integration-test            # install integration test dependencies
make tokenserver                 # install tokenserver utility dependencies
```

Lint / format / type-check:

```bash
make ruff-lint                   # ruff check tools/
make ruff-format                 # ruff format tools/
make ruff-fmt-chk                # format diff check
make mypy                        # mypy type checking
make pydocstyle                  # docstring validation
make bandit                      # security linting
```

## Integration / E2E tests

### Local (against a running server)

```bash
# Run all integration tests except real FxA e2e
PYTHONPATH=$(pwd)/tools \
SYNC_MASTER_SECRET=secret0 \
SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL=http://localhost:6000 \
TOKENSERVER_HOST=http://localhost:8000 \
poetry -C tools/integration_tests run pytest . --ignore=tokenserver/test_e2e.py
```

Or via the Makefile shortcut (sets the env vars above):

```bash
make run_local_e2e_tests
```

### Docker-based E2E (full stack)

```bash
make docker_run_mysql_e2e_tests
make docker_run_postgres_e2e_tests
make docker_run_spanner_e2e_tests
```

Each command spins up two compose stacks (with and without JWK cache), copies
JUnit XML results out of the container, then tears everything down.

## Running the server locally

```bash
# MySQL
make run_mysql

# Spanner (requires GCP service account key at ./service-account.json)
make run_spanner
```

Server listens on `http://localhost:8000`. Health check: `GET /__heartbeat__`.

## Key environment variables

| Variable | Purpose |
|---|---|
| `SYNC_MASTER_SECRET` | HAWK token signing secret |
| `SYNC_SYNCSTORAGE__DATABASE_URL` | Syncstorage DB connection string |
| `SYNC_TOKENSERVER__DATABASE_URL` | Tokenserver DB connection string |
| `SYNC_TOKENSERVER__NODE_TYPE` | `mysql` / `postgres` / `spanner` |
| `SYNC_TOKENSERVER__RUN_MIGRATIONS` | Auto-migrate on startup (`true`/`false`) |
| `SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL` | FxA OAuth endpoint |
| `RUST_TEST_THREADS` | Set to `1` for all test runs |
| `RUST_LOG` | Log level (`debug`, `info`, etc.) |
| `SYNC_TEST_LOG_HTTP` | Set any value to log all HTTP req/resp in integration tests |
| `SYNC_SERVER_URL` | Integration test base URL (default: `http://localhost:8000`) |
| `TOKENSERVER_HOST` | Tokenserver host for integration tests |
| `PYTHONPATH` | Must point to `$(pwd)/tools` when running integration tests |

## Docker Compose files

| File | Purpose |
|---|---|
| `docker/docker-compose.mysql.yaml` | MySQL syncstorage + MySQL tokenserver |
| `docker/docker-compose.postgres.yaml` | PostgreSQL syncstorage + PostgreSQL tokenserver |
| `docker/docker-compose.spanner.yaml` | Spanner emulator + MySQL tokenserver |
| `docker/docker-compose.e2e.{mysql,postgres,spanner}.yaml` | E2E test runner overlay |
| `docker/docker-compose.e2e.jwk-cache.yaml` | Enable JWK caching overlay |
| `docker/docker-compose.e2e.no-jwk-cache.yaml` | Disable JWK caching overlay |

## Documentation

```bash
make doc-install-deps            # install mdBook + mdBook-mermaid
make doc-watch                   # live preview at localhost
make doc-prev                    # build and serve
make doc-test                    # validate markdown

make api-prev                    # generate OpenAPI spec + serve Swagger UI on :8080
```


## Test structure

- `tools/integration_tests/conftest.py` โ€” pytest fixtures only (`st_ctx`)
- `tools/integration_tests/helpers.py` โ€” retry helpers, auth state, `switch_user`
- `tools/integration_tests/test_storage.py` โ€” storage protocol tests
- `tools/integration_tests/tokenserver/conftest.py` โ€” tokenserver fixtures only
- `tools/integration_tests/tokenserver/helpers.py` โ€” tokenserver DB/auth helpers
- `tools/integration_tests/tokenserver/test_*.py` โ€” tokenserver tests

## MCP integrations

Two MCP servers are configured for this repository: **Atlassian Jira** (`mcp__atlassian__`) and **Sentry** (`mcp__sentry__`).

Use the skills below to query them. Do not call MCP tools ad-hoc without reading the shared context first.

| Skill | Invocation | Purpose |
|---|---|---|
| Jira status | `/jira-status [me\|epic KEY\|epics\|sprint\|summary ...\|create ...\|update STOR-#### ...]` | Task landscape, epics, personal view, summaries, create/edit tickets |
| Sentry health | `/sentry-health [new\|regressions\|volume\|full]` | Production error trends, regressions, new issues |

**Shared context:** `.claude/mcp-context.md` contains project IDs, JQL patterns, Sentry project slug, what to flag, and known noise. Skills read this at runtime. Read it before any ad-hoc MCP query.

**Identity when using these tools:** You are acting as both engineer and engineering manager. Surface patterns, not just raw lists. Distinguish "needs action now" from "worth monitoring". Do not fabricate data โ€” if a query returns nothing, say so.

**Jira project:** `STOR` โ€” `https://mozilla-hub.atlassian.net`
**Sentry project:** `syncstorage-prod` โ€” org `mozilla`