The server
The Spring Boot 4 service behind Switchyard: the admin API the dashboard uses, and the SDK API client libraries use. How to run and configure it is in the main README; the full API is in PLAN.md.
Code layout
| Package | What |
|---|---|
project |
Projects and their environments, SDK key rotation |
flag |
Flags (boolean or multivariate), on/off per environment, targeting (Targeting) and the checks that need the flag's project (TargetingValidator) |
segment |
Segments: reusable groups of contexts that flag rules target |
browser |
/client/v1: browser flags' values for a public client-side ID, evaluated with the Java client |
sdk |
/sdk/v1/flags and /sdk/v1/stream: the in-memory SDK cache (SdkService), its database reads (SdkStore), the SSE streams (StreamHub) and the change events that drive them |
audit |
The append-only change history |
auth |
The filter that checks admin tokens, sign-in sessions and SDK keys; GitHub sign-in (SignInController, GitHubClient, Accounts); roles (AccessGuard, called by every service before it changes anything) and members; /api/me; secret hashing |
common |
Errors as RFC 9457 problem details, CORS for the dashboard, shared constants |
Database migrations are in src/main/resources/db/migration (Flyway): V1__init.sql for the schema, V2__targeting.sql for rules and rollouts, V3__accounts.sql for GitHub sign-in and undo, V4__flag_model.sql for variations, prerequisites and segments, V5__client_side.sql for client-side IDs, V6__roles.sql for roles and protected environments.
Building the container image
Dockerfile builds the GraalVM native image and puts it on a slim Debian base. Build from the repository root, since it needs the Java client too: docker build -f server/Dockerfile -t switchyard-server .. CI publishes it to GitHub Container Registry on every push to main; docs/DEPLOY.md runs it on Render.
How a change reaches apps
- An admin request changes a flag. In the same transaction, the environment's
versiongoes up by one and the change is written to the audit log. - After the transaction commits,
SdkServiceupdates its cached version for the environment, thenStreamHubsendsevent: versionto that environment's open streams. - Clients fetch
/sdk/v1/flagswith their last ETag. The payload comes from memory: the database isn't touched on the SDK path except to fill the cache.
Everything after the commit happens inside this process, which assumes a single server instance. See the design decisions in PLAN.md.
Tests
./mvnw -f ../clients/java/pom.xml install -DskipTests # the contract tests use the real Java client
./mvnw verifyIntegration tests run Postgres 17 in Docker (Testcontainers) and are skipped when Docker isn't running. GitHubSignInTests stands up a fake GitHub with the JDK's HTTP server. StreamLoadTests (1,000 clients) only runs on request:
./mvnw verify -Dtest=StreamLoadTests -Dswitchyard.load=trueOn Windows, stop any running copy of the server first: it locks target/*.jar, and packaging fails after the tests pass.