Internal contribution guide
This page is for contributors and coding agents working on the S3 Lens repository. Operator-facing auth details live in Authorization reference; this guide focuses on code structure and workflow.
The AGENTS.md file in the repository root mirrors much of this guidance for automated tools.
Monorepo layout
Section titled “Monorepo layout”S3 Lens is a Cargo workspace (Rust backend + policy engine) and a pnpm workspace (runtime-neutral web UI, Electron shell, shared contracts, and docs).
flowchart TB subgraph root ["Repository root"] cfg["s3lens.yaml.example<br/>.env.example"] compose["docker-compose.yml"] pkg["package.json scripts"] agents["AGENTS.md"] end
subgraph rust ["Cargo workspace"] bin["crates/s3lens<br/>HTTP API, auth, S3 clients"] policy["crates/s3lens-policy<br/>PolicyEngine, CompiledPolicy"] macros["crates/s3lens-macros<br/>#[enforce], #[public]"] end
subgraph js ["pnpm workspace"] web["apps/web<br/>React UI → embedded in binary"] desktop["apps/desktop<br/>Electron workspaces + stable proxy"] contracts["packages/contracts<br/>Effect Schema runtime DTOs"] docs["apps/docs<br/>Starlight site (this site)"] end
subgraph scripts ["scripts/"] garage["garage-bootstrap-container.sh"] kc["keycloak-bootstrap.sh"] end
web -->|"build.rs include_bytes!"| bin contracts --> web contracts --> desktop bin --> policy bin --> macros policy --> macros pkg --> compose pkg --> garage compose --> kc
style bin fill:#1e3a5f,color:#fff style policy fill:#1e3a5f,color:#fff| Path | Owns |
|---|---|
crates/s3lens/src/s3/ |
S3 client abstraction — keep AWS SDK types here, not in route handlers |
crates/s3lens/src/routes/ |
Axum handlers and OpenAPI annotations |
crates/s3lens/src/auth/ |
Sessions, OIDC login, middleware, handler enforcement |
crates/s3lens-policy/ |
Policy compilation and evaluation — identity-agnostic |
apps/web/src/lib/api/ |
Frontend API wrappers — must match backend JSON shapes |
apps/desktop/ |
Electron-owned server workspaces, local process, health gate, and s3lens://app proxy |
packages/contracts/ |
Effect Schema API response DTOs and isolated private desktop IPC |
Runtime architecture
Section titled “Runtime architecture”At runtime a single s3lens binary serves the embedded SPA, /api/* JSON routes, and OpenAPI docs. Storage operations go through per-provider S3 clients configured in s3lens.yaml.
flowchart LR browser["Browser"] axum["Axum server<br/>(crates/s3lens)"] ui["Embedded UI<br/>(apps/web/dist)"] api["/api/* handlers"] engine["PolicyEngine<br/>(s3lens-policy)"] s3["S3 clients<br/>(per provider)"] store["Garage / SeaweedFS / AWS …"]
browser --> axum axum --> ui axum --> api api --> engine api --> s3 s3 --> store
style engine fill:#1e3a5f,color:#fffThe optional desktop shell keeps storage traffic runtime-agnostic. apps/web uses
relative URLs; its trusted bundled Electron surface receives a desktop bridge for
the Workspace selector and in-window Workspace settings. These components are guarded
with isElectron, so browser-hosted S3 Lens stays unchanged. Electron selects and
health-checks a workspace before loading the website. Vite or packaged assets
always serve the stable s3lens://app origin; only /api/* is proxied to the
managed local process or remote S3 Lens API.
sequenceDiagram participant E as Electron participant P as Desktop controller participant S as S3 Lens server participant W as Website E->>P: Load workspace catalog P-->>E: Start connection attempt E->>S: GET /api/health capabilities alt unhealthy E->>P: Show error and retry/change else healthy E->>W: Load s3lens://app/ W->>S: Relative /api via protocol proxy endWorkspace switching prepares an isolated candidate partition and commits it only
after health, authentication, protocol, and renderer readiness succeed. The
desktop controller owns onboarding and recovery; the attached application owns its
desktop-only sidebar Workspace selector and /settings/workspaces management page.
Build pipeline:
flowchart LR pnpm["pnpm --filter @s3lens/web build"] dist["apps/web/dist"] buildrs["crates/s3lens/build.rs"] cargo["cargo build"] binary["s3lens binary"]
pnpm --> dist --> buildrs --> cargo --> binaryAuth and policy modules
Section titled “Auth and policy modules”Policy evaluation is split from identity. The s3lens-policy crate never reads cookies or OIDC claims; the s3lens auth module wires principals to grants.
flowchart TB subgraph config ["s3lens.yaml"] pol["policy:"] roles["roles:"] authCfg["auth:"] end
subgraph policyCrate ["crates/s3lens-policy"] load["PolicyEngine::load"] compiled["CompiledPolicy"] eval["PolicyEngine::allowed"] end
subgraph authMod ["crates/s3lens/src/auth"] identity["identity.rs<br/>Principal, SessionStore, BindingResolver"] login["login.rs<br/>OIDC, local users, routes"] enforce["enforce.rs<br/>inject_auth_context, require()"] end
subgraph handlers ["Route handlers"] macro["#[enforce(action, …)]"] manual["require() / require_each_key()"] end
pol --> load roles --> load load --> compiled authCfg --> identity identity --> enforce login --> enforce compiled --> eval enforce -->|"Grants once per request"| eval macro --> enforce manual --> enforce
style eval fill:#1e3a5f,color:#fff style enforce fill:#1e3a5f,color:#fffStartup and per-request flow in more detail: Authorization reference — end-to-end flow.
Where to make changes
Section titled “Where to make changes”Use this when deciding which crate or directory to edit:
flowchart TD start(["What are you changing?"])
start --> apiChange{HTTP route or<br/>request/response shape?} apiChange -->|yes| routes["crates/s3lens/src/routes/<br/>+ openapi.rs<br/>+ apps/web/src/lib/api/"] apiChange -->|no| s3Change{S3 provider<br/>compatibility?} s3Change -->|yes| s3dir["crates/s3lens/src/s3/<br/>+ provider-compatibility docs"] s3Change -->|no| polChange{Policy rule format,<br/>actions, or evaluation?} polChange -->|yes| polcrate["crates/s3lens-policy/<br/>tests/fixtures/"] polChange -->|no| authChange{Login, session,<br/>or handler checks?} authChange -->|yes| authdir["crates/s3lens/src/auth/<br/>+ s3lens-macros if macro changes"] authChange -->|no| uiChange{UI only?} uiChange -->|yes| webdir["apps/web/<br/>(follow backend API)"] uiChange -->|no| docsChange{User or operator docs?} docsChange -->|yes| docsdir["apps/docs/src/content/docs/"] docsChange -->|no| devChange{Local dev stack?} devChange -->|yes| devfiles["docker-compose.yml<br/>scripts/<br/>package.json<br/>s3lens.yaml.example"]
style polcrate fill:#1e3a5f,color:#fff style authdir fill:#1e3a5f,color:#fffDeferred unless explicitly requested: Helm charts, runtime provider CRUD, IAM Condition blocks, AWS STS assume-role.
Local development
Section titled “Local development”First-time setup from a clean checkout:
flowchart TD compose["docker compose up -d"] garage["garage bootstrap container"] kc["keycloak-bootstrap container"] dev["pnpm dev"]
compose --> garage compose --> kc
style garage fill:#14532d,color:#fff style kc fill:#14532d,color:#fff| Command | Purpose |
|---|---|
docker compose up -d |
Start and bootstrap Garage, SeaweedFS, and Keycloak |
docker compose down |
Stop dependencies and preserve volumes |
docker compose down -v |
Reset local dependency data |
pnpm dev |
Rust watch mode on :8085 + Vite HMR on :3000 |
pnpm dev:server |
Rust server only |
pnpm dev:web |
Vite HMR only |
pnpm dev:desktop |
Electron + Vite + debug Rust binary |
The Garage bootstrap container applies cluster layout, imports the local-only s3lens key, creates my-bucket, and grants bucket access. Keycloak realm/users are created inside Compose. The standard stack needs no .env file or shell setup.
flowchart LR subgraph policyTests ["s3lens-policy"] fixtures["tests/fixtures/policies/*.yaml"] intTests["tests/*.rs<br/>(engine, rules, selector, …)"] end
subgraph binTests ["s3lens"] authSmoke["tests/auth/"] authInt["tests/auth_integration.rs"] wiring["tests/policy_wiring.rs"] ignored["tests/local_s3_compat.rs<br/>(#[ignore], needs compose)"] end
fixtures --> intTests authSmoke --> authInt| Change type | Run |
|---|---|
| Policy engine | cargo test -p s3lens-policy |
| Backend / auth | cargo test -p s3lens |
| Web UI | vp run @s3lens/web#test && vp run @s3lens/web#build |
| Desktop shell | vp run @s3lens/desktop#typecheck && vp run @s3lens/desktop#test && (cd apps/desktop && vp pack) |
| Docs | pnpm --filter @s3lens/docs build |
| Release binary | pnpm build or pnpm build:release |
Policy tests use real YAML fixture files under crates/s3lens-policy/tests/fixtures/ — prefer adding fixtures over large inline test strings in src/.
Conventions checklist
Section titled “Conventions checklist”- Pin dependencies exactly (
Cargo.tomlworkspace deps,pnpm-workspace.yamlcatalogs). - Install JS deps with
sfw vp install --ignore-scriptsfrom the repo root. - Keep Electron awareness out of
apps/web; desktop workspaces and IPC stay inapps/desktop. - Keep API DTO schemas in
packages/contracts; decode JSON at the focused web API boundary. - Never create a module, directory, or package named
http. - Keep S3 SDK usage inside
crates/s3lens/src/s3/; handlers use the internal client types. - Protected handlers:
#[enforce(...)]orrequire()/require_each_key()for loops. - Public routes:
#[public]— no route-level policy middleware; registration is plain Axum.route(). - Do not commit
s3lens.yaml,.env, or generateddist/directories. - Conventional Commits; small logical commits; do not bypass commit signing.
Related docs
Section titled “Related docs”| Page | Audience |
|---|---|
| Authorization reference | Route-to-action mapping, permission flow |
| Access policies | Writing YAML policy rules |
| Authentication | OIDC and local user configuration |
| Storage providers | Garage, SeaweedFS, AWS compatibility notes |