Oxide Web Console

repository·main·Indexed 42 days ago

https://github.com/oxidecomputer/console

A client-side React application serving as a web interface for the Oxide API. Built with Vite and TanStack Query, it provides a thin, transparent layer over the API with minimal client-side state and cookie-based authentication. The project includes a generic UI library in `app/ui` and a generated API client based on the Nexus OpenAPI spec.

Tokens
13.8K
Snippets
45
Records
58
Agent score
53%

What's inside Oxide Web Console

  1. Understand Content Security Policy (CSP) implementation

    main

    The Oxide Web Console uses Content Security Policy (CSP) headers in both local development (via Vite) and production (via Vercel) to identify potential directive breaks early in the development lifecycle.

    Base Directives

    The following directives are applied to align with OWASP Secure Headers best practices:

    • default-src 'self': Restricts all resources to the same origin by default.
    • style-src 'unsafe-inline' 'self': Allows same-origin CSS and inline styles (note: 'unsafe-inline' is intended for eventual removal).
    • frame-src 'none': Disallows <frame> and <iframe> elements.
    • object-src 'none': Disallows <object> and <embed> elements.
    • form-action 'none': Disallows traditional HTML form submissions (the console uses JS-based POSTs instead).
    • frame-ancestors 'none': Prevents click-jacking by disallowing the site from being embedded in iframes.

    Development-Specific Behavior

    In development mode, an additional script-src directive is added that references a randomly-generated nonce. This nonce is injected by Vite into the generated index.html, allowing dev-mode scripts to execute without enabling 'unsafe-inline', keeping the development environment as close to production security as possible.

  2. How the mock API works

    main

    The mock API is built using the same code that generates the real API client, ensuring the mock's shape matches the OpenAPI specification.

    Key characteristics:

    • Automated Validation: Handlers for endpoints (e.g., create project) use generated Zod validators to parse request bodies, providing accurate 400 Bad Request responses for invalid data.
    • Manual Logic: While the request parsing is automated, the core business logic (e.g., checking if a name is unique or saving to the database) is implemented manually via typed callbacks.
    • In-Memory Database: The mock API uses an in-memory "database" located at mock-api/msw/db.ts. This is a JavaScript object where each "table" is an array of models.
    • Type Safety: Because model types and runtime validators are generated from the OpenAPI spec, the mock API returns data that matches the real API's structure as long as the typechecker passes.
  3. Understand the purpose of UI components in `app/ui`

    main

    The components located in app/ui are designed to be generic, reusable UI elements that could exist in a standalone Oxide UI library. Unlike components in app/components, these are intentionally decoupled from application-specific logic. They are (mostly) unaware of:

    • React Router (with the exception of a few Link components)
    • React Hook Form
    • React Query

    Use app/ui for building foundational, presentation-focused UI elements that do not require direct integration with the application's routing, form state, or data fetching layers.

  4. How the Web Console architecture works

    main

    The Oxide Web Console is a fully client-side React application designed to minimize client-side state and act as a transparent view of the Oxide API.

    Key Architectural Details:

    • No SSR: To avoid the complexity of server-side rendering on the rack, the app is built as a set of static assets (index.html, JS, CSS) using Vite.
    • Deployment: Assets are served from specific console endpoints in Nexus.
    • Authentication: The console uses cookie-based authentication. Logging in sets a cookie, and subsequent API requests are made using that cookie.
    • API Integration: The API client is generated from the Nexus OpenAPI spec using oxide.ts. In the codebase, the generated client and its React Query wrapper are accessible via the @oxide/api alias.
  5. Best practices for pinning Omicron versions

    main

    When pinning API versions in the Oxide Web Console, follow these guidelines to ensure stability:

    • Pinning in main: Only pin main commits of Omicron when working in the console main branch.
    • Temporary pins: You may temporarily pin to a commit from an unmerged API branch for development in a feature branch, but do not merge the console PR until the corresponding Omicron PR is merged and the console's pinned version is updated to a main commit.
    • Commit hashes vs. Branch names: Always pin a specific commit hash rather than a branch name. Pinning a branch name is unreliable because the console branch will not automatically track updates to that branch. Instead, pin the specific hash and update it manually when the API branch progresses.
  6. Install and set up the Oxide Web Console development environment

    main

    To develop the Oxide Web Console, ensure you are using Node.js v24+ and npm v12+. If your Node version includes an older npm, upgrade it using npm install --global npm@12.

    Follow these steps to install dependencies:

    1. Run npm install to install project dependencies.
    2. Run npx playwright install to install the browsers required for end-to-end (E2E) testing.
    npm install
    npx playwright install
  7. Update the pinned API version

    main

    To update the API version used by the console, you must update the pinned Omicron commit hash and regenerate the API client. Failure to regenerate the client will cause safety tests in app/api to fail.

    Follow these steps:

    1. Update the OMICRON_VERSION environment variable (or configuration) with the new Omicron commit hash.
    2. Regenerate the API client by running npm run gen-api. This command automatically checks out the specific Omicron commit specified in OMICRON_VERSION.
    3. Run npm run tsc to check for type errors introduced by the new API definitions. If errors occur, fix them in the UI code.
    4. If you have added new endpoints, you must add them to the handler map in mock-api/msw/handlers.ts. Use NotImplemented for new endpoints if the UI does not yet require them.
    5. Commit and push your changes.
    # 1. Update OMICRON_VERSION (manual step)
    # 2. Regenerate API client
    npm run gen-api
    
    # 3. Check types
    npm run tsc
  8. Access the Console in a browser

    main

    Once Nexus is running and configured with the correct static_dir, you can access the Console by navigating to <nexus_address>/login in your browser.

    If running locally with the default example configuration, the address is typically http://localhost:12220/login. Note that the root route (/) will return a 404.

  9. Build Console assets manually

    main

    If you are making changes to the Console and want to test them locally against Nexus, you must build the static assets yourself.

    Dependencies:

    • Node.js (16+ recommended, 14+ supported)

    To build the assets, run the following commands in the console directory:

    npm install && npm run build
  10. Run the Vite dev server with MSW mock API

    main

    This is the standard development mode. It uses Mock Service Worker (MSW) to run a mock API directly in the browser. The app is available at http://localhost:4000 and supports Hot Module Replacement (HMR).

    To switch to a non-default user (e.g., Hans Jonas who lacks fleet viewer permissions), set the msw-user cookie in your browser console:

    document.cookie = 'msw-user=Hans Jonas;domain=localhost;path=/'
    npm run dev
  11. Run E2E tests with Playwright

    main

    End-to-end tests are located in test/e2e/.

    • npm run e2e: Runs tests in Chrome, Firefox, and Safari.
    • npm run e2ec: Runs tests in Chrome only (faster, recommended for local dev).
    • npm run e2e -- --ui: Opens Playwright's UI mode for interactive debugging.

    To debug CI failures locally, use the provided debug script: ./tools/debug-ci-e2e-fail.sh.

    npm run e2ec
  12. Use pre-built Console assets via Omicron

    main

    For most use cases, you do not need to build the Console assets yourself. Assets from commits on main are available as tarballs at dl.oxide.computer/releases/console/<sha>.tar.gz.

    A specific console version is pinned in Omicron in the file tools/console_version. The ./tools/install_prerequisites.sh script will automatically download and extract the correct tarball to the appropriate location.

    To use a different commit than the one pinned:

    1. Change the commit hash and SHA256 in tools/console_version.
    2. Retrieve the new SHA256 from https://dl.oxide.computer/releases/console/<commit>.sha256.txt.
    3. Re-run the ./tools/install_prerequisites.sh script.