Guidance for AI coding agents working in the Floci UI repository.
This file defines repository-specific operating rules for autonomous or semi-autonomous coding agents. Follow these instructions unless a maintainer explicitly tells you otherwise.
AGENTS.md is the canonical agent-instructions file for this repository, following the
AGENTS.md standard. CLAUDE.md, GEMINI.md, and
.github/copilot-instructions.md are symlinks to this file — edit AGENTS.md only.
Floci UI is the web console / DevTools for Floci, the local multi-cloud emulator. It is an AWS-Console-style UI for a locally running cloud runtime.
It does not emulate anything itself. The frontend renders cloud resources; the API translates the UI's REST/JSON requests into cloud-SDK calls against the locally running Floci emulators (AWS, Azure, GCP).
- pnpm workspace monorepo, two packages:
packages/frontend— React + Vite + TypeScript, served on port4500packages/api— Bun + Hono + AWS SDK v3, served on port4501
- Emulator endpoints it talks to: Floci core (AWS)
:4566, Floci-AZ:4577, Floci-GCP:4588
When making changes, follow these priorities:
- Use real cloud-provider contracts — never invent custom backend endpoints for UI convenience
- Reuse the schema-driven multi-cloud pattern instead of bespoke per-service code
- Keep the frontend talking only to
/api/*; never reach a cloud endpoint directly from the browser - Prefer real empty states over fake/sample data
- Keep changes narrow and testable
Critical rules:
- Do not add custom protocols just for the UI unless the core project accepts that contract
- Do not have the frontend call AWS/Azure/GCP endpoints directly — always go through
packages/api - Do not introduce decorative data or fake operational metrics — unwired states stay empty
- Do not perform broad refactors unless the task explicitly requires them
Browser (React/Vite :4500)
→ /api/* (Hono, Bun :4501)
→ CloudProxyService → CloudAdapterRegistry → CloudServiceAdapter
→ AWS SDK v3 (:4566) | Floci-AZ HTTP (:4577) | Floci-GCP HTTP (:4588)
The repo is mid-migration from an older AWS-only per-service style to a newer schema-driven, multi-cloud generic explorer. The generic pattern is the one to use for all new work; the legacy routes survive only for deep EC2 panels and Secrets Manager.
packages/api/src/cloud-spi/serviceCatalog.ts— the single source of truth for which services exist.CloudServiceTypederives from its keys; nav metadata (display name, icon hint, group, route) is served to the frontend from here.packages/api/src/cloud-spi/types.ts—CloudProvider(aws|azure|gcp), theCloudServiceAdapterinterface,ServiceSchema, and the status shapes.packages/api/src/cloud-spi/errors.ts— the typed errors adapters throw; mapped to HTTP once inroutes/clouds.ts(withadapter-aws/awsErrors.tsfor SDK failures).packages/api/src/registry/CloudAdapterRegistry.ts— registry keyed by"cloud:service". Availability is derived from it, so registering an adapter is what lights up the nav.packages/api/src/service/CloudProxyService.ts— the single dispatcher.packages/api/src/service/runtimeProbe.ts— per-runtime liveness probes.packages/api/src/cloudProxy.ts— where adapters are instantiated and registered.packages/api/src/routes/clouds.ts— the generic/api/clouds/...REST surface.packages/api/src/cloudProxy.test.ts— guards that no schema advertises a capability its adapter cannot perform.
A ServiceSchema (fields, actions, capabilities, filters, columns) drives the UI:
the frontend's DynamicResourceView renders list / create / delete / inspect generically
from the schema — most services need no bespoke UI.
packages/frontend/src/App.tsx— routes (/console/:cloud,/cloud-explorer/:cloud/:service)packages/frontend/src/components/Layout.tsx— nav, rendered fromGET /clouds/:cloud/servicespackages/frontend/src/api/queries/cloudQueries.ts— shared cloud/service/status queriespackages/frontend/src/components/serviceIcons.ts—iconKey-> component, with a fallbackpackages/frontend/src/components/DynamicResourceView.tsx— schema → table/form/inspector orchestrator- Reusable:
ResourceTable,DynamicFormRenderer,ResourceInspector,StorageObjectBrowser,CosmosNoSqlPanel,EmptyState,lib/capabilities.ts - API client:
src/api/cloudProxyClient.ts,src/api/api.ts,src/api/HttpClient.ts
packages/api/src/routes/{ec2,rds,eks,secretsmanager}.ts and the matching
features/ec2/* frontend code. New services go through the generic SPI, not here.
pnpm install
pnpm dev # API (:4501) + frontend (:4500) together
pnpm dev:api # API only
pnpm dev:web # frontend only
Requires a running Floci core (:4566) — see README.md / docker compose (use the
multicloud profile to also start Azure + GCP).
pnpm lint # eslint, frontend
pnpm type-check # tsc on both packages
pnpm test # bun test, packages/api
pnpm build # production build
This is the canonical pattern (also referenced by the open service-coverage issues).
Backend (packages/api) — this is the whole change:
- Add one row to
SERVICE_CATALOGinsrc/cloud-spi/serviceCatalog.ts(only for a new category).CloudServiceType, the route guard, and the nav metadata all derive from it. src/cloud-spi/<service>Schema.ts— export a per-cloud<cloud><Service>Schema()returning aServiceSchema. Model:src/cloud-spi/storageSchema.ts. Usepathon a column to surface ametadata.*field.src/adapter-<cloud>/<Cloud><Service>Adapter.ts implements CloudServiceAdapterwith a.test.tsalongside. Model:src/adapter-aws/AwsStorageAdapter.ts. AWS adapters use AWS SDK v3 againstFLOCI_ENDPOINT; Azure/GCP adapters take the shared runtime client (AzureRuntimeClientinazure.ts,GcpRuntimeClientingcp.ts) — do not hand-roll fetch.- Register it in
src/cloudProxy.ts.
That is it. services() derives availability from the registry, schema() serves only
registered adapters, and the generic /api/clouds/... routes need no new handler.
Frontend (packages/frontend): normally nothing.
The nav, Console Home, and Cloud Explorer render GET /clouds/:cloud/services. Optional:
add an iconKey to components/serviceIcons.ts (an unknown key falls back to a generic
icon, so this is cosmetic), and add a service === '<x>' panel in DynamicResourceView
only for deep UX (models: ComputePanel, NetworkingPanel, CosmosNoSqlPanel).
Rules:
- Copy an existing adapter + schema before introducing a new shape.
- Throw the typed errors in
src/cloud-spi/errors.ts, never a bareError—routes/clouds.tsmaps them to HTTP and no longer matches on message text. - Never advertise a capability the adapter cannot perform.
src/cloudProxy.test.tsfails the build if a capability markedavailablehas no adapter method, or if anything notavailablelacks areason. UsedescriptorOverride()when the adapter exists but the local runtime does not implement it. - Regenerate the README table when navigation changes:
cd packages/api && bun run scripts/service-matrix.ts.
- TypeScript throughout; prefer self-explanatory code over comments
- Match the surrounding code's naming, structure, and idiom
- Frontend: function components + hooks; data fetching via the existing React Query wrappers
- Keep controllers/routes thin; put logic in adapters/services
- Follow existing project patterns; introduce new patterns only when they clearly improve clarity
- API tests use
bun test(packages/api); colocate*.test.tsnext to the adapter - Add or update tests for any change to request handling, response shape, or adapter behavior
- Documentation, formatting, or low-risk refactors may not need new tests, but the existing
suite plus
pnpm lint,pnpm type-check, andpnpm buildmust still pass - If you change behavior without adding coverage, say why in the PR
- Keep PRs focused — one feature or fix per PR; avoid unrelated refactors
- Branch off
main; open the PR againstmain - The PR title must follow Conventional Commits
(it becomes the squash-merge commit). Scopes identify the package or service area
(
frontend,api,s3,serverless,docker,ci, …) - Keep README service-status notes accurate; add verification notes for newly wired operations
Do not add Co-Authored-By trailers for AI tools in commit messages. Keep attribution
limited to human contributors.
Releases are tag-driven. Docker images are never published on PR merge — only when a
maintainer pushes an X.Y.Z tag, which triggers .github/workflows/release.yml to build
and push the multi-arch floci/floci-ui image. Treat release workflows as critical infra.
- Adding custom backend endpoints instead of using real cloud contracts / the generic SPI
- Calling cloud endpoints directly from the frontend instead of through
/api/* - Adding fake/sample data instead of real empty states
- Extending the legacy
ec2/rds/eks/secretsmanagerroutes for new work - Forgetting to register the adapter in
cloudProxy.ts— that registration is what makes the service appear; do not hardcode availability in the frontend - Advertising a schema capability the adapter cannot perform (
cloudProxy.test.tscatches it) - Throwing a bare
Errorfrom an adapter instead of a typed error fromcloud-spi/errors.ts - Skipping
pnpm type-check/pnpm testbefore finishing
If behavior is unclear, prefer the real cloud-provider contract, then the existing Floci UI convention, then the corresponding emulator's behavior. If a task would require broad architectural change, stop and surface the tradeoffs instead of refactoring blindly.