Skip to content

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.

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

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:#fff

The 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
end

Workspace 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 --> binary

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:#fff

Startup and per-request flow in more detail: Authorization reference — end-to-end flow.

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:#fff

Deferred unless explicitly requested: Helm charts, runtime provider CRUD, IAM Condition blocks, AWS STS assume-role.

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/.

  • Pin dependencies exactly (Cargo.toml workspace deps, pnpm-workspace.yaml catalogs).
  • Install JS deps with sfw vp install --ignore-scripts from the repo root.
  • Keep Electron awareness out of apps/web; desktop workspaces and IPC stay in apps/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(...)] or require() / 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 generated dist/ directories.
  • Conventional Commits; small logical commits; do not bypass commit signing.
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