diff --git a/content/docs/advanced/architecture/agents.mdx b/content/docs/advanced/architecture/agents.mdx index b76bcf4..c9e71d8 100644 --- a/content/docs/advanced/architecture/agents.mdx +++ b/content/docs/advanced/architecture/agents.mdx @@ -10,29 +10,36 @@ An **Agent** is a lightweight service that runs on the target host where your Do Before deploying the Agent, you need to register it in the Hub to obtain an authentication token. 1. Navigate to the **Agents** page in the OrcaCD UI. -2. Click **Add Agent** and give it a name (e.g., `production`, `staging`). -3. Copy the generated `AUTH_TOKEN` (only shown once). +2. Click **Add Agent** and give it a name (e.g., `production`, `staging`) and optionally an icon. +3. The setup dialog shows a `compose.yml` for the Agent that already contains `HUB_URL`, the generated `AUTH_TOKEN` and an Agent image matching the version of the Hub. Copy it with **Copy**, or copy only the token with **Copy agent auth token** in the menu next to it. The token is only shown once. +4. Start the Agent (see below). The dialog waits for the first connection and shows **Connected** as soon as the Agent is online. If the Agent connects but cannot reach Docker, the dialog shows an error instead. ![Create Agent](/assets/docs/create-agent.png?url) +`HUB_URL` in the generated file is the address you used to open the UI. If the Agent reaches the Hub through a different URL, change it before starting the Agent. + ## Deploying an Agent ### Same Host as the Hub -The default `docker-compose.yml` includes both Hub and Agent. If you are running both on the same machine, just set the `AUTH_TOKEN` in your `.env` and restart: +The default `docker-compose.yml` includes both Hub and Agent. If you are running both on the same machine, set the `AUTH_TOKEN` in your `agent.env` and recreate the Agent: ```bash -docker compose up -d +docker compose up -d agent ``` ### Remote Host -To deploy an Agent on a separate server, copy only the Agent service definition to that machine. See [installation](/docs/setup/installation) for instructions on how to set up the Agent service. +To deploy an Agent on a separate server, save the `compose.yml` from the setup dialog on that machine and start it with `docker compose up -d`. If you prefer to keep the token out of the Compose file, move `HUB_URL` and `AUTH_TOKEN` into an `agent.env` file and reference it with `env_file`, as in the [default compose file](https://github.com/OrcaCD/orca-cd/blob/main/docker-compose.yml). The Agent will connect to the Hub. Once connected, its status in the Hub UI changes to **Online**. The Agent needs access to the Docker socket to manage containers on the host. +### Hub with a Private CA + +If the Hub uses a self-signed certificate or a certificate from a private CA, mount the CA certificate into the Agent container and set `CA_CERT_FILE` to its path. The certificates are added to the system CAs. See [environment variables](/docs/configuration/env-variables#agent). + ## Multiple Agents You can register as many agents as you need. For example, one per environment: @@ -45,21 +52,58 @@ You can register as many agents as you need. For example, one per environment: Each Application in OrcaCD is assigned to exactly one Agent. You can split workloads across agents freely. +## How the Agent Deploys + +For every application, the Agent creates a directory `//` (by default `/deployments//` inside the Agent container) containing: + +- `compose.yaml`: the Compose file from your repository. +- `compose.override.yaml`: generated by OrcaCD to mark the containers as managed by OrcaCD. The file is rewritten on every deployment, so manual changes are overwritten. + +The Compose project name is derived from the application name. The Agent does not touch Compose projects that were not created by OrcaCD. + +Before deploying, the Agent checks the Compose file against the [deployment policy](/docs/advanced/security#compose-deployment-policy). + ## Rotating an Agent Token -If a token is compromised or needs to be rotated, navigate to the Agent in the UI, open the options menu, and select **Rotate Token**. A new token is generated immediately and the old token becomes invalid. +If a token is compromised or needs to be rotated, navigate to the Agent in the UI, open the options menu, and select **Rotate token**. A new token is generated immediately, the old token becomes invalid and a running Agent is disconnected right away. -Update the `AUTH_TOKEN` in the Agent's `.env` and restart it: +Update the `AUTH_TOKEN` in the Agent's `agent.env` (or its Compose file) and recreate the container: ```bash -docker compose up -d +docker compose up -d agent ``` +## Deleting an Agent + +Deleting an Agent removes it and all of its applications (including their history) from the Hub and disconnects the Agent. This also works while the Agent is offline, e.g. if the host no longer exists. + + + Deleting an Agent does **not** remove the containers of its applications from the host. If the + containers should be removed, delete the applications first while the Agent is still online + (applications can not be deleted while their Agent is offline), or remove them manually on the + host afterwards. + + ## Agent Status -| Status | Meaning | -| --------- | --------------------------------------------------- | -| `online` | Agent is connected and ready to receive deployments | -| `offline` | Agent has disconnected or has not yet connected | +| Status | Meaning | +| --------- | --------------------------------------------------------------------------------------------- | +| `Online` | Agent is connected and ready to receive deployments | +| `Offline` | Agent has disconnected or has not yet connected | +| `Error` | Agent is connected but cannot reach its Docker daemon. Deployments fail until it is reachable | If an Agent is offline, deployment dispatch fails immediately and the application is marked out-of-sync with an error. Deployments are not queued or retried automatically. Trigger the deployment again once the Agent is back online. + +### Connection + +The Hub sends a ping to all Agents every 60 seconds. If the Agent does not hear from the Hub for about 150 seconds, it drops the connection and reconnects. When the Hub restarts, all Agents are marked offline until they reconnect. + +When an Agent disconnects, the health of its applications changes to `Unknown`, and deployments that were still running are marked as failed. + +Offline [notifications](/docs/advanced/notifications) are only sent after the Agent has been disconnected for 60 seconds, so short reconnects (e.g. Agent restarts or token rotation) do not trigger them. + +## Versions + +The Agents page shows the Agent and Docker version reported by each Agent. Agents that are older than the Hub are marked as outdated, and Agents older than the minimum version supported by the Hub as unsupported. See [Upgrading](/docs/setup/upgrading) for how to update an Agent. + +![Agents page with versions and status](/assets/docs/agents-page.png?url) diff --git a/content/docs/advanced/architecture/applications.mdx b/content/docs/advanced/architecture/applications.mdx index c8b9b29..69f2a6c 100644 --- a/content/docs/advanced/architecture/applications.mdx +++ b/content/docs/advanced/architecture/applications.mdx @@ -5,22 +5,25 @@ description: Creating and managing applications in OrcaCD An **Application** in OrcaCD represents a Docker Compose deployment. It links a repository (and a specific branch and file path) to an Agent, which runs the actual containers. +## Overview Dashboard + +The **Overview** page shows the status of all applications, recent deployments and agents at a glance and lists the applications that need your attention, e.g. because they are unhealthy or their last deployment failed. + +![Overview dashboard](/assets/docs/overview-dashboard.png?url) + ## Creating an Application -1. Navigate to the **Applications** page and click **Add Application**. -2. Fill in the required fields: +Navigate to the **Applications** page, click **New Application** and select the Agent, the repository, the branch and the Compose file. The name must be unique per Agent, because the Docker Compose project name is derived from it. + +OrcaCD reads the Compose file from the latest commit of the branch and immediately deploys it to the selected Agent. - | Field | Description | - | -------------- | ---------------------------------------------------------------------------------------------- | - | **Name** | Display name for the application | - | **Repository** | The repository containing the Compose file | - | **Branch** | The branch to track (e.g., `main`, `production`) | - | **Path** | Path to the `docker-compose.yml` inside the repository (e.g., `apps/myapp/docker-compose.yml`) | - | **Agent** | The agent that will execute the deployment | +## Application Details -3. Click **Create**. +The details page of an application shows its [status](#application-status), the repository, the deployed commit, the Agent and what triggered the last sync. Below, you can see the current Compose file and a diff to the previous version. -OrcaCD will immediately sync the repository and deploy the Compose file to the selected Agent. +![Application details with the Deploy dropdown](/assets/docs/application-details.png?url) + +![Diff of the Compose file](/assets/docs/application-manifest-diff.png?url) ## Application Status @@ -28,62 +31,96 @@ Each application shows two status indicators: ### Sync Status -| Status | Meaning | -| ------------- | ---------------------------------------------------- | -| `Synced` | Deployed commit matches the latest repository commit | -| `Out Of Sync` | A new commit exists that has not been deployed yet | -| `Syncing` | A deployment is currently in progress | -| `Unknown` | No deployment has been triggered yet | +| Status | Meaning | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `Synced` | The last deployment, sync or image update succeeded | +| `Out of Sync` | The last deployment, sync or image update failed or was interrupted (e.g. the Agent disconnected or the Hub restarted during it) | +| `Syncing` | A deployment or image update is currently in progress | +| `Unknown` | No deployment has finished yet | + +New commits are not shown as out of sync: as soon as OrcaCD sees a new commit (depending on the [sync strategy](/docs/advanced/architecture/repositories#sync-strategies) of the repository), it deploys it automatically. If an application is `Out of Sync`, check the error on the details page or in the [history](#history) and deploy it again. ### Health Status -| Status | Meaning | -| ----------- | ---------------------------------------------------- | -| `Healthy` | All containers from the Compose file are running | -| `Unhealthy` | One or more containers are stopped or in a bad state | -| `Unknown` | Health has not been checked yet | +The health is reported by the Agent, which watches the Docker events of the application's containers and updates the status in real time. + +| Status | Meaning | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------- | +| `Healthy` | All containers are running and none of them reports a failing Docker healthcheck | +| `Unhealthy` | At least one container is not running (e.g. restarting) or reports a failing Docker healthcheck | +| `Stopped` | All containers are created, exited or paused, e.g. after `docker compose stop` | +| `Unknown` | No containers exist, a healthcheck is still starting, a deployment is running, the Agent is disconnected or cannot reach Docker | ## Triggering a Deployment -OrcaCD automatically deploys when the tracked repository branch receives a new commit (depending on the repository sync strategy). You can also trigger a deployment manually: +OrcaCD automatically deploys when the tracked repository branch receives a new commit that changes the Compose file (depending on the repository sync strategy), and when image polling or the image pull webhook detects a new image. You can also trigger a deployment manually from the details page: -1. Open the application. -2. Click **Deploy** in the top-right corner. +| Action | Description | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| **Deploy** | Redeploys the current Compose file without syncing the repository | +| **Sync repository** | Available in the dropdown next to **Deploy**. Fetches the latest commit and deploys the changes to all applications of this repository | +| **Pull images** | Available in the dropdown next to **Deploy**. Pulls the latest images and redeploys only if an image changed | -This forces an immediate sync and deployment regardless of the current sync status. + + The Agent must be online to deploy. If it is offline, the deployment fails immediately and the + application is marked `Out of Sync` with an error. Deployments are not queued or retried + automatically, so trigger the deployment again once the Agent is back online. + -## Application Settings +## History + +The **History** page of an application records every deployment, commit sync and image update with its time, trigger, the user who triggered it, the commit, the result (`Running`, `Succeeded`, `Failed` or `No change`) and error details. + +You can switch between a table and a timeline view. The search filters the history by type, trigger, result, user, commit hash or message, and error message. + +![Application history in the timeline view](/assets/docs/application-history-timeline.png?url) -Open an application and navigate to the **Settings** tab to configure it. +## Application Settings ### General -Change the application name, icon, linked repository, branch, path, or agent. +Change the name, icon, Agent, repository, branch and Compose file of the application, or delete it. + +Saving does not deploy the application. If you change the Compose file, the Agent or the name, a notification with a **Deploy** button appears so you can apply the changes right away. + +![Deploy prompt after saving changes](/assets/docs/deploy-after-edit-toast.png?url) + +When you move the application to another Agent or rename it so that its Compose project name changes, OrcaCD first removes the old containers from the current Agent. Deleting an application also removes its containers and deployment directory from the Agent. Both require the current Agent to be online. Otherwise the request fails with `409 Conflict` and nothing is changed. + +![General settings of an application](/assets/docs/application-general-settings.png?url) + +#### Delete old images + +When enabled, OrcaCD removes the images an application no longer uses after every successful deployment, image update and manual image pull. Images are removed by ID, so this works both for changed tags and for new digests of the same tag. Images that are still used by other containers are kept. -### Image Polling +### Image Updates -Image polling lets OrcaCD automatically pull updated Docker images and restart containers, even when no Compose file has changed. This is useful for continuous delivery workflows where a CI pipeline pushes new image tags. +![Image update settings](/assets/docs/application-image-updates.png?url) -| Setting | Description | -| ------------------------ | ---------------------------------------------------------------------------- | -| **Enable image polling** | Turns image polling on or off | -| **Polling interval** | How often to check for new images (minimum 30 seconds, default 120 seconds) | -| **Delete old images** | When enabled, old images are removed from the host after a successful update | +#### Image Polling -## Image Pull Webhooks +Image polling lets OrcaCD automatically pull updated Docker images and restart containers, even when the Compose file has not changed. This is useful for mutable tags like `latest` or for CI pipelines that push new images under the same tag. -Instead of polling, you can trigger an image pull from your container registry via a webhook. This is ideal for registry-side push notifications. +The polling interval is at least 60 seconds (default: 120 seconds). -1. Open the application settings. +The Agent compares the images of the application with the registry. If any image changed, it pulls the images and redeploys the application. + +If the check fails, for example because the Agent has no credentials for a private registry, the application changes to `Out of Sync` and the error is recorded in its history. Repeated failures with the same error are only recorded once. The next successful check sets the application back to `Synced`. See [common issues](/docs/troubleshooting/common-issues#deployment-is-stuck-in-syncing) for registry credentials. + +#### Image Pull Webhook + +Instead of polling, your container registry can notify OrcaCD via a webhook when a new image is pushed. + +1. Open the **Image Updates** settings of the application. 2. In the **Image Pull Webhook** section, click **Generate Webhook**. -3. OrcaCD returns a webhook URL and secret. -4. Register this webhook in your container registry (e.g., Docker Hub, GitHub Container Registry, Harbor). +3. Copy the secret. It is only shown once. The webhook URL is shown afterwards in the settings. +4. Register the webhook in your container registry. See [Webhooks](/docs/configuration/webhooks#container-registries) for the setup of Docker Hub, GitHub Packages, Harbor and generic registries. + +When a webhook is received, the Agent pulls all images of the application and redeploys it if any image changed. Deliveries for the same application are combined: the pull starts once no further delivery has arrived for 5 seconds, but at the latest 30 seconds after the first one. This way, one push that triggers several webhooks only results in one deployment. -When the registry sends a push event to the webhook URL, OrcaCD instructs the Agent to pull the latest image and restart the affected container. +Click **Regenerate** to create a new secret or **Revoke** to disable the webhook. The old secret stops working immediately. The Agent must be online for the image pull webhook to succeed. If the Agent is offline, the event is not queued. - -To revoke a webhook and generate a new one, click **Revoke Webhook** and then **Generate Webhook** again. diff --git a/content/docs/advanced/architecture/overview.mdx b/content/docs/advanced/architecture/overview.mdx index c111564..3f8e0b5 100644 --- a/content/docs/advanced/architecture/overview.mdx +++ b/content/docs/advanced/architecture/overview.mdx @@ -11,12 +11,14 @@ This architecture allows for a scalable and flexible deployment system that can ### Hub The Hub is the central component of OrcaCD that manages the state of the system, processes deployment instructions, and provides an API for the Agents and other clients to interact with the system. -Additionally, the Hub serves the web UI and provides a REST API for external integrations. It is responsible for processing deployment instructions, managing the state of the system, and providing an interface for the Agents to communicate with. +Additionally, the Hub serves the web UI and provides a REST API for external integrations. ### Agent The Agent is responsible for executing deployments and reporting back to the Hub. It runs on the target environment where the applications are deployed and communicates with the Hub to receive deployment instructions and send status updates. +The Agent watches the Docker events of the deployed containers and continuously reports the health of the applications to the Hub. It also reports its own version, the Docker version and whether it can reach the Docker daemon. +See [Agents](/docs/advanced/architecture/agents) for details. ### Communication @@ -31,13 +33,15 @@ OrcaCD follows the GitOps model, where the desired state of the system is define ### Git Syncing -OrcaCD supports multiple Git syncing strategies, including polling and webhooks, to keep the Hub in sync with the desired state defined in Git. -This allows for a seamless integration with existing Git workflows and tools. +OrcaCD supports multiple ways to keep the Hub in sync with the desired state defined in Git: polling, webhooks from the Git provider, manual syncs from the UI or API, and [GitHub Actions](/docs/guides/github-actions). +This allows for a seamless integration with existing Git workflows and tools. See [Repositories](/docs/advanced/architecture/repositories#sync-strategies) for details. ## Server-Sent-Events (SSE) -In order to provide real-time updates to the UI and other clients, OrcaCD uses Server-Sent-Events (SSE) to stream events from the Hub to the web ui. -This allows for a responsive and interactive user experience, as clients can receive updates about the state of deployments, agents, and other system components in real-time without the need for polling. +In order to provide real-time updates to the UI, OrcaCD uses Server-Sent-Events (SSE) to stream events from the Hub to the web UI. +The events do not contain any state. Instead, the Hub sends an `update` event with a list of API URLs whose data has changed (e.g. `/api/v1/applications`), and the UI refetches the data it currently displays for these URLs. +Several changed resources can be combined into a single event. +This keeps the UI up to date with deployments, agents, and other system components in real-time without the need for polling. ## Persistence diff --git a/content/docs/advanced/architecture/repositories.mdx b/content/docs/advanced/architecture/repositories.mdx index 2fbdc6c..b14463a 100644 --- a/content/docs/advanced/architecture/repositories.mdx +++ b/content/docs/advanced/architecture/repositories.mdx @@ -3,47 +3,49 @@ title: Repositories description: Connecting and managing Git repositories in OrcaCD --- -A **Repository** in OrcaCD is a Git repository that contains Docker Compose files. OrcaCD keeps the repository in sync with its remote and uses it as the source of truth for your deployments. +A **Repository** in OrcaCD is a Git repository that contains Docker Compose files. OrcaCD keeps your applications in sync with the repository and uses it as the source of truth for your deployments. + +OrcaCD does not clone the repository. It reads branches, the file tree, commits and Compose files through the API of the Git provider. ## Supported Providers - GitHub -- GitLab +- GitLab (GitLab.com or self-hosted) - Gitea - Azure DevOps -- Bitbucket -- Generic (Git) - -## Adding a Repository -1. Navigate to the **Repositories** page and click **Add Repository**. -2. Enter the repository URL (e.g., `https://github.com/your-org/your-repo`). -3. Select the provider. -4. Choose an authentication method. -5. Choose a sync strategy. -6. Save. + + Bitbucket and generic Git repositories are not supported yet. They are shown in the UI but cannot + be selected. + -OrcaCD will immediately attempt to clone the repository and perform an initial sync. +## Adding a Repository -## Authentication Methods +Navigate to the **Repositories** page, click **Add Repository** and enter the repository URL (e.g., `https://github.com/your-org/your-repo`), an optional [auth token](#authentication) and the [sync strategy](#sync-strategies). OrcaCD tests the connection before the repository is added. For the webhook sync type, the webhook secret is shown at the end. Copy it, it is only shown once. -### None +![Add Repository dialog](/assets/docs/create-repository.png?url) -For public repositories, no credentials are required **but recommended** for security. +OrcaCD stores the URL without a trailing `/` and without a `.git` suffix. Adding a repository whose URL and sync type match an existing repository fails with `409 Conflict`. -### Token (HTTPS) +## Authentication -Use a personal access token or deploy token. Enter the token in the **Auth Token** field. +If you enter an auth token, OrcaCD uses token authentication. Without a token, the repository is accessed without credentials, which only works for public repositories. For GitHub, create a [fine-grained personal access token](https://github.com/settings/tokens) with read access to **Contents**. For GitLab, create a [project access token](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) with `read_repository` scope. +You can change or remove the token later in the **Auth** settings of the repository. Leave the field blank to remove authentication. OrcaCD tests the connection before saving. + ## Sync Strategies +The sync type can be changed later in the **Edit Sync** settings of the repository. + +![Repository sync settings](/assets/docs/repository-sync-settings.png?url) + ### Polling -OrcaCD periodically checks the remote repository for new commits. The minimum polling interval is **30 seconds**; the default is **60 seconds**. +OrcaCD periodically checks the remote repository for new commits. The default polling interval is **60 seconds**. In the UI, the interval can be set between 60 seconds and 24 hours. Use polling when: @@ -52,23 +54,59 @@ Use polling when: ### Webhook -OrcaCD listens for push events from your Git provider via webhook. This is more efficient than polling and reacts to new commits instantly. +OrcaCD listens for push events from your Git provider via webhook. This is more efficient than polling and reacts to new commits instantly. See [Webhooks](/docs/configuration/webhooks#git-providers) for the setup with your Git provider. + +To not reveal whether a repository exists, the webhook endpoint returns the same `401` response for unknown repositories, wrong secrets and repositories without the webhook sync type. Webhook requests are rate limited. ### Manual The repository is only synced when you explicitly trigger a sync from the UI or API. +### What Happens on a Sync + +Independent of the sync type, you can always sync a repository manually with **Sync** on the Repositories page or **Sync repository** on the details page of an application. + +On every sync, OrcaCD fetches the latest commit of each branch used by the applications of the repository. If the Compose file of an application changed, OrcaCD deploys it. Otherwise, only the new commit is recorded. + ## GitHub Actions OIDC -For GitHub repositories, you can enable **GitHub Actions OIDC** integration. This allows GitHub Actions workflows to trigger deployments directly without storing a long-lived token in your CI secrets. +For GitHub repositories, you can enable **GitHub Actions Access** in the sync settings of the repository. This allows GitHub Actions workflows to trigger deployments directly without storing a long-lived token in your CI secrets. + +In the same place you can restrict the allowed actions, branches and workflows. See the [GitHub Actions guide](/docs/guides/github-actions) for details. + +## Signed Commits + +To protect against a compromised Git account, you can require that OrcaCD only deploys commits that are signed by a trusted key. Open the settings of the repository, go to **Signing** and enable **Require signed commits**. Then add the public keys of everyone who is allowed to push deployable commits: + +- SSH public keys, one per entry (e.g. `ssh-ed25519 AAAA... user@example.com`) +- Armored GPG public key blocks (`-----BEGIN PGP PUBLIC KEY BLOCK-----`) + +At least one key is required, and up to 50 keys are allowed per repository. Keys are configured per repository. + +![Signed commits settings](/assets/docs/repository-signing.png?url) + +When the setting is enabled, OrcaCD checks the commit before anything is deployed: for polling, webhooks, GitHub Actions, manual syncs and when an application is created or updated. If the commit is not signed by a trusted key, the sync fails, a [notification](/docs/advanced/notifications) is sent and the history of the application records the rejected commit. + +How the signature is checked depends on the provider: + +- **GitHub and Gitea**: OrcaCD verifies the signature itself. +- **GitLab**: OrcaCD accepts a commit if GitLab reports the signature as verified and the key is on the trusted list. The key also has to be added to the GitLab account of the signer. GPG keys are matched by their key ID. +- **Azure DevOps**: not supported, because its API does not provide signature data. + + + The `allowed_signers` format with an email address in front of the key, SSH certificates and X.509 + signatures are not supported. Use plain SSH public keys or GPG keys. + + +## Deleting a Repository -See the [GitHub Actions guide](/docs/guides/github-actions) for details. +Deleting a repository also deletes all of its applications. OrcaCD first removes the applications from their Agents, so all affected Agents must be online. If an Agent is offline or fails to remove an application, the deletion stops with an error. ## Repository Status | Status | Meaning | | --------- | ---------------------------------- | -| `success` | Last sync completed without errors | -| `syncing` | Sync is in progress | -| `failed` | Last sync failed | -| `unknown` | Repository has not been synced yet | +| `Success` | Last sync completed without errors | +| `Syncing` | Sync is in progress | +| `Failed` | Last sync failed | +| `Unknown` | Repository has not been synced yet | diff --git a/content/docs/advanced/cli.mdx b/content/docs/advanced/cli.mdx index a0cc204..10a8c01 100644 --- a/content/docs/advanced/cli.mdx +++ b/content/docs/advanced/cli.mdx @@ -13,6 +13,8 @@ When using Docker, prefix commands with: docker compose exec hub /app/hub ``` +Use `docker compose exec` while the Hub is running. Commands that require a stopped Hub (`import` and `key rotate`) are executed with `docker compose run --rm hub ` instead. The image entrypoint is already `/app/hub`, so the binary path must not be repeated there. + Most commands do not require an interactive terminal. For commands that require confirmation `--yes`/`-y` can be used as CLI flag to confirm dangerous operations. @@ -30,21 +32,23 @@ hub export [--output ] | ---------------- | ------------------------------- | ------------------------------- | | `-o`, `--output` | `hub-export-YYYYMMDD-HHMMSS.db` | Output path for the backup file | +Relative paths are resolved inside the `data/` directory, absolute paths are used as given. The command refuses to overwrite an existing file. + **Usage with Docker:** ```bash docker compose exec hub /app/hub export ``` -The file is created inside the container under `/app/data/`. Copy it out of the container to include it in your backup strategy: +The file is created inside the container under `/app/data/`, which is mounted to `./data/hub/` on the host with the default `docker-compose.yml`. Copy it from there to include it in your backup strategy: ```bash -docker compose cp hub:/app/data/hub-export-*.db ./backups/ +cp ./data/hub/hub-export-*.db ./backups/ ``` ### import -Restores a database from a backup file. The existing database is copied to a rollback file before the restore, so you can recover if something goes wrong. +Restores a database from a backup file. The existing database is copied to a rollback file (`-restore-.bak`) before the restore, so you can recover if something goes wrong. Backups created by a newer OrcaCD version are rejected. The Hub server must be **stopped** before running import. All existing data is overridden by the @@ -63,14 +67,14 @@ hub import [--yes] **Usage with Docker:** ```bash -# Copy the backup into the container -docker compose cp ./backups/hub-export-20240101-120000.db hub:/app/data/ +# Copy the backup into the data directory of the hub +cp ./backups/hub-export-20240101-120000.db ./data/hub/ # Stop the hub docker compose stop hub # Run import -docker compose run --rm hub /app/hub import data/hub-export-20240101-120000.db +docker compose run --rm hub import data/hub-export-20240101-120000.db # Restart the hub docker compose up -d hub @@ -106,22 +110,38 @@ docker compose stop hub # 3. Rotate the key (set the NEW APP_SECRET in .env first) docker compose run --rm \ -e OLD_APP_SECRET= \ - hub /app/hub key rotate + hub key rotate # 4. Restart the hub with the new APP_SECRET docker compose up -d hub ``` -After a successful rotation, all agent tokens and user sessions signed with the old secret are invalidated. Users need to log in again and agents need their tokens re-issued). +After a successful rotation, all agent tokens and user sessions signed with the old secret are invalidated. Users need to log in again and agents need their tokens re-issued. ### reset-password Resets a user's password and forces them to change it on the next login. See [Account Recovery](/docs/troubleshooting/account-recovery) for full details. ```bash -hub reset-password +hub reset-password --email +hub reset-password --id ``` +| Flag | Description | +| --------- | ------------------------- | +| `--email` | Email address of the user | +| `--id` | ID of the user | + +Exactly one of `--email` or `--id` must be provided. Users that only log in via SSO can not be reset. + +### version + +Prints the version of the Hub. Also available as `--version`/`-v`. + +### healthcheck + +Checks whether the Hub is running and healthy. This is used by the Docker healthcheck of the image. + ## Agent -The Agent CLI does not currently provide any helpful commands for end users. +The Agent CLI only provides the `version` and `healthcheck` commands. The `healthcheck` command is used by the Docker healthcheck of the image and reports the Agent as healthy once it is connected to the Hub. diff --git a/content/docs/advanced/notifications.mdx b/content/docs/advanced/notifications.mdx index 06dbe7a..0abd1ec 100644 --- a/content/docs/advanced/notifications.mdx +++ b/content/docs/advanced/notifications.mdx @@ -1,9 +1,9 @@ --- title: Notifications -description: Setting up deployment notifications in OrcaCD +description: Setting up notifications for applications, agents and repositories in OrcaCD --- -OrcaCD can send notifications when a deployment succeeds or fails. Notifications can be scoped to specific applications or applied globally. +OrcaCD can send notifications when something happens to your applications, agents or repositories, for example when a deployment fails, an application becomes unhealthy or an agent goes offline. Each notification selects the events it should be sent for and the resources it covers. ## Supported Providers @@ -15,76 +15,98 @@ OrcaCD can send notifications when a deployment succeeds or fails. Notifications - Generic Webhook - Custom (Shoutrrr) +## Events + +There are 11 events across three resource types. A notification only fires for an event if the event is selected **and** the resource it happened to is covered by the notification (see [Notification Scope](#notification-scope)). + +### Application Events + +| Event | Fires when | Example message | +| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| **Deployment succeeded** | The agent reports that a deployment was applied successfully. | `Success: deployment succeeded for my-app` | +| **Deployment failed** | The agent reports that a deployment failed. | `Error: deployment failed for my-app` | +| **Image update succeeded** | Image polling pulled new images for the application, or an explicitly triggered image update (registry webhook, manual or GitHub Action) finished. Periodic polls that find no new images stay silent. | `Success: image update succeeded for my-app` | +| **Image update failed** | Pulling the application's images failed, both for periodic image polling and for explicitly triggered image updates. | `Error: image update failed for my-app` | +| **Sync failed** | The hub detected a new commit but couldn't hand the deployment to the agent, for example because the Compose file couldn't be fetched from the repository or the agent is not connected. | `Error: sync failed for my-app` | +| **Became unhealthy or stopped** | The application was **healthy** and is now reported as unhealthy or stopped. It does not fire when the previous health was unknown (for example right after an agent reconnects) or while the application is syncing. | `Application my-app is unhealthy` / `is stopped` | +| **Healthy again** | The application was unhealthy or stopped and is now reported as healthy again. | `Application my-app is healthy again` | + +### Agent Events + +| Event | Fires when | Example message | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | +| **Went offline (after 1 minute)** | The agent has been disconnected for 60 seconds. Shorter interruptions, such as agent restarts or network blips, stay silent. | `Agent my-agent is offline` | +| **Back online** | The agent reconnects after an offline notification was sent for it. Reconnects without a prior offline notification stay silent. | `Agent my-agent is back online` | + + + The hub keeps track of which agents were reported offline in memory only. After a hub restart, no + **Back online** notification is sent for agents that were reported offline before the restart. + + +### Repository Events + +| Event | Fires when | Example message | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | +| **Sync failed** | A repository sync fails after the previous sync succeeded. Repeated failures (for example on every polling interval) don't notify again. | `Error: sync failed for repository my-repo: ` | +| **Sync recovered** | A repository sync succeeds after the previous sync failed. | `Success: sync recovered for repository my-repo` | + ## Creating a Notification -1. Navigate to **Notifications** and click **Add Notification**. -2. Choose a provider type and enter a name. -3. Fill in the provider-specific configuration (see below). -4. Optionally link the notification to specific applications. -5. Save. +Navigate to **Notifications**, click **Add Notification** and follow the dialog. In the events step, all events are preselected but no resources are, so pick resources or turn on the "All ..." switch in every tab you want notifications for (see [Notification Scope](#notification-scope)). -Use the **Test** button to send a test message and verify that the configuration is correct. +Before saving, the hub sends a test notification with the entered configuration. The notification is only saved if the test message was delivered. Otherwise, the error is shown in the dialog and nothing is saved. -## Provider Configuration +## Managing Notifications -### Discord +After creation, only **Enabled**, the selected events and the scope can be edited. To change the name, type or provider configuration, delete the notification and create a new one. **Send Test** in the row menu also works for disabled notifications. -Fill in these fields in the notification form: +The **Status** column shows the result of the last delivery attempt. Delivery errors are logged by the hub. Credentials are removed from error messages: URLs are reduced to their scheme and host, so webhook tokens, passwords and API keys are not exposed. -| Field | Required | Description | -| --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| **Webhook URL** | yes | Full webhook URL, copied from your Discord server under **Server Settings → Integrations → Webhooks → (select webhook) → Copy Webhook URL** | -| **Bot Name** | no | Override the bot display name | -| **Avatar URL** | no | Override the bot avatar URL | -| **Thread ID** | no | Send to a specific thread | +## Notification Scope -### Gotify +The events and the covered resources are selected per resource type in the **Applications**, **Agents** and **Repositories** tabs. Either turn on the "All ..." switch to cover every resource of that type, including ones created later, or select individual resources. -| Field | Required | Description | -| --------------- | -------- | ---------------------------------------------------------- | -| **Server URL** | yes | Full URL of your Gotify instance (http or https) | -| **App Token** | yes | Application token, created in the Gotify UI under **Apps** | -| **Priority** | yes | Message priority, between -2 and 10 (suggested default: 5) | -| **Custom Path** | no | Custom path prefix if Gotify is behind a sub-path proxy | +When the "All" switch is on, any individually selected resources of that type are discarded on save. If you turn the switch off again later, you need to select the resources again. -### Slack +A notification is sent when the notification is enabled, the event is selected, and the affected resource is covered by the switch or the picker of the matching tab. For example, a notification with **Deployment failed** selected and **All Applications** turned on, but no agent events selected, only reports failed deployments. -| Field | Required | Description | -| --------------- | -------- | --------------------------------------------------------------------------------------------------------------------- | -| **Webhook URL** | yes | Create an [Incoming Webhook](https://api.slack.com/messaging/webhooks) in your Slack workspace and paste its URL here | +![Notification events and scope](/assets/docs/create-notification.png?url) -### Email (SMTP) +You can combine notifications: use one notification with the "All" switches for a global channel, and add separate notifications that cover only specific resources for channels that should only receive specific alerts. -| Field | Required | Description | -| ---------------- | -------- | ----------------------------------------------- | -| **SMTP Host** | yes | SMTP server hostname (no scheme or port) | -| **SMTP Port** | yes | SMTP port (e.g., 587 for STARTTLS, 465 for TLS) | -| **From Address** | yes | Sender email address | -| **To Addresses** | yes | Recipient email addresses, one per line | -| **Username** | no | SMTP authentication username | -| **Password** | no | SMTP authentication password | -| **From Name** | no | Sender display name | -| **Use TLS** | no | Use STARTTLS (default: on) | + + Existing notifications are migrated with the events that were sent before events became + selectable: deployment succeeded/failed, image update succeeded/failed and application sync + failed. Health, agent and repository events are **not** enabled automatically. **Enable by + Default** was replaced by **All Applications**, which is only turned on if the notification was + linked to every existing application. Otherwise, the notification keeps its linked applications. + Review your notifications after upgrading. + -### Microsoft Teams +## Network Access -| Field | Required | Description | -| --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Webhook URL** | yes | Create an [Incoming Webhook connector](https://support.microsoft.com/en-US/Workflows/send-messages-in-teams-using-incoming-webhooks) in your Teams channel and paste its URL here | -| **Title** | no | Custom message title | +All notification traffic goes through OrcaCD's SSRF protection, including SMTP connections. Connections to private, loopback and link-local addresses are blocked. If you run a self-hosted target on a private IP, for example a Gotify server, an SMTP relay or a webhook receiver in your local network, add its IP or CIDR range to [`ALLOWED_INTERNAL_IPS`](/docs/configuration/env-variables). -### Generic Webhook +## Provider Configuration + +Most providers only need a webhook URL or the server and credentials of the service. Some notes: -Sends a JSON POST request to any HTTP/HTTPS endpoint. +- **Discord**: Copy the webhook URL under **Server Settings → Integrations → Webhooks**. +- **Slack**: Create an [Incoming Webhook](https://api.slack.com/messaging/webhooks). Only `https://hooks.slack.com/services/...` URLs are accepted. The bot name and icon are configured in your Slack app. +- **Microsoft Teams**: Create an [Incoming Webhook connector](https://support.microsoft.com/en-US/Workflows/send-messages-in-teams-using-incoming-webhooks) in your channel. The URL must use `https`. +- **Gotify**: Create an application token under **Apps** in the Gotify UI. +- **Email (SMTP)**: Enter the host without scheme or port. **Use TLS** enables STARTTLS and is on by default. Multiple recipients can be separated by commas or new lines. +- **Generic Webhook**: Sends a `POST` request with `Content-Type: application/json` and the notification text in the `message` field. Additional headers are entered one per line as `Header-Name: value`. -| Field | Required | Description | -| --------------- | -------- | --------------------------------------------------------------------- | -| **Webhook URL** | yes | Target URL (http or https) | -| **Headers** | no | Additional HTTP request headers, one per line as `Header-Name: value` | +```json title="Generic Webhook body" +{ + "message": "Error: deployment failed for my-app" +} +``` ### Custom (Shoutrrr) -For any service supported by [Shoutrrr](https://shoutrrr.nickfedor.com/) that doesn't have a dedicated provider, enter the raw Shoutrrr URL directly. +For any service supported by [Shoutrrr](https://shoutrrr.nickfedor.com/) that doesn't have a dedicated provider, enter the raw Shoutrrr URL directly. The URL must use a Shoutrrr service scheme, not `http` or `https`. ``` pushover://token@user @@ -92,15 +114,3 @@ ntfy://topic@ntfy.sh ``` Refer to the [Shoutrrr documentation](https://shoutrrr.nickfedor.com/) for the URL format of each service. - -## Notification Scope - -### Enable by Default - -When **Enable by Default** is on, the notification is sent for all applications. Including ones created in the future, unless explicitly unlinked. - -### Per-Application - -When **Enable by Default** is off, the notification is only sent for the applications listed in the **Applications** field. - -You can mix both approaches: use "enable by default" for a global notification channel, and add separate per-application notifications for channels that should only receive specific alerts. diff --git a/content/docs/advanced/security.mdx b/content/docs/advanced/security.mdx index 993cbc3..21bc37e 100644 --- a/content/docs/advanced/security.mdx +++ b/content/docs/advanced/security.mdx @@ -7,16 +7,60 @@ OrcaCD is designed to be safe by default, but of course, there are always additi ## Harden OrcaCD -- Disable password authentication for the Hub and use a secure OIDC provider instead, that enforces strong authentication methods, including secure multi-factor authentication (MFA). +- Disable password authentication for the Hub and use a secure OIDC provider instead, that enforces strong authentication methods, including secure multi-factor authentication (MFA). See the [OIDC guide](/docs/guides/oidc). - Always run the hub behind a secure reverse proxy and ensure that all communication is encrypted using TLS. -- Make sure to configure the `TRUSTED_PROXIES` environment variable correctly to prevent IP spoofing attacks. -- Restrict the IPs that can access the hub by configuring the `ALLOWED_IPS` environment variable. This will prevent unauthorized access to the hub and does not apply to webhooks or the agent connections. +- Make sure to configure the `TRUSTED_PROXIES` environment variable correctly to prevent IP spoofing attacks. Without it, the IP-based features (`ALLOWED_IPS` and rate limiting) only see the IP of your reverse proxy. +- Restrict the IPs that can access the hub by configuring the `ALLOWED_IPS` environment variable. This will prevent unauthorized access to the hub. It does not apply to webhooks, GitHub Actions, the health check and the agent connections. - Restrict the volumes that can be mounted to locations within the OrcaCD deployments directory by setting the `RESTRICT_VOLUMES_TO_DEPLOYMENTS_DIR` environment variable to `true`. This will prevent malicious applications from accessing sensitive files on the host system. +- Keep `ALLOWED_PRIVILEGED_APPS` as short as possible. Applications listed there skip all checks of the [deployment policy](#compose-deployment-policy). +- Treat `APP_SECRET` and the agent `AUTH_TOKEN` values like passwords. +- Give the Agent its own env file (`agent.env` in the default `docker-compose.yml`) and never pass it the `.env` file of the Hub. See [Agent environment](#agent-environment). +- Enable [signed commits](/docs/advanced/architecture/repositories#signed-commits) for repositories, so that a compromised Git account can not deploy changes. + +All environment variables are described on the [environment variables page](/docs/configuration/env-variables). ## Why is it safe by default? A big focus of OrcaCD next to ease of use is security. Here are some of the measures we have taken to achieve this: -- All sensitive data is stored encrypted in the database using a modern encryption algorithm ([AEGIS-256](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead-18)). -- Messages between the Hub and the Agents are encrypted with the same algorithm. The key is computed using the quantum-resistant [ML-KEM](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf) algorithm in combination with [X25519](https://www.rfc-editor.org/rfc/rfc7748.html). -- We take all security issues seriously. You can find our security policy [here](https://github.com/OrcaCD/orca-cd/blob/main/SECURITY.md). +### Hub and Agent communication + +Every Agent connection performs its own encrypted handshake in addition to TLS: + +- The session key is negotiated with a hybrid key exchange of the quantum-resistant [ML-KEM-768](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf) and [X25519](https://www.rfc-editor.org/rfc/rfc7748.html). Both shared secrets are combined with HKDF-SHA256, and the resulting key is bound to the ID of the Agent. +- Hub and Agent authenticate each other with Ed25519 signatures. The Agent verifies the Hub with the public key contained in its `AUTH_TOKEN`. The Agent signs its part of the handshake with its own private key, which is also part of the `AUTH_TOKEN` and is never sent over the network. +- All messages except keep-alive pings are encrypted with [AEGIS-256](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead-18). Each message contains the role of the sender and a counter, so messages can not be replayed or reflected back to their sender. + +### Data at rest + +Sensitive database fields, such as repository credentials, webhook secrets, OIDC client secrets, notification settings and Compose files, are encrypted with AEGIS-256. The key is derived from `APP_SECRET`. To change the secret, use [`hub key rotate`](/docs/advanced/cli#key-rotate). + +### Authentication and sessions + +- The first admin account can only be created with a one-time setup code that is printed to the Hub logs. See [installation](/docs/setup/installation). +- Passwords are hashed with Argon2id. +- The Hub checks every request against the database, so role changes and deleted users take effect immediately. Open live update connections are checked every 30 seconds. +- Changing or resetting a password signs the user out on all other devices. Under **Settings → Security → Sessions**, users can end all of their sessions with **Sign out everywhere**. Disabling or deleting an OIDC provider ends the sessions of all users linked to it. +- Requests that change data are only accepted if their `Origin` header matches `APP_URL` (CSRF protection). Webhooks and GitHub Actions are excluded, as they are authenticated by a secret or an OIDC token. +- The Hub sends a strict Content Security Policy and other security headers, which, for example, prevent the UI from being embedded in other sites. +- OIDC logins use PKCE and a nonce. An SSO login is not linked to an account whose user changed the email address themselves, see [OIDC](/docs/guides/oidc#how-sso-users-are-handled). + +![Sign out everywhere](/assets/docs/security-sessions.png?url) + +### Webhooks + +Webhooks return the same `401 Unauthorized` response for unknown IDs, wrong secrets and disabled webhooks. This way they do not reveal whether a repository or application exists. + +### Outbound requests (SSRF protection) + +Requests the Hub sends to user-configured URLs, for example to Git providers, notification services and OIDC providers, are blocked if they connect to a private, loopback, link-local or otherwise reserved IP address. This is also checked for redirects, for hostnames that resolve to such an address and for IPv4 addresses embedded in NAT64 and 6to4 IPv6 addresses. If you run one of these services in your internal network, allow its IP or range with `ALLOWED_INTERNAL_IPS`. + +### Agent environment + +Compose files can read the environment of the Agent through variable interpolation (e.g. `${AUTH_TOKEN}`). To prevent applications from reading secrets this way, the Agent removes `AUTH_TOKEN` from its environment after startup. It also removes `APP_SECRET` and `OLD_APP_SECRET` of the Hub and logs a warning if it finds them, for example because Hub and Agent share the same `.env` file. + +All other variables of the Agent stay available to deployed Compose files, so only put settings into the Agent's env file that applications are allowed to read. + +### Security policy + +We take all security issues seriously. You can find our security policy [here](https://github.com/OrcaCD/orca-cd/blob/main/SECURITY.md). diff --git a/content/docs/advanced/tailscale.mdx b/content/docs/advanced/tailscale.mdx new file mode 100644 index 0000000..f29daea --- /dev/null +++ b/content/docs/advanced/tailscale.mdx @@ -0,0 +1,229 @@ +--- +title: Tailscale +description: Run OrcaCD privately in your tailnet with Tailscale +--- + +With [Tailscale](https://tailscale.com) you can run the OrcaCD Hub without exposing it to the public internet. The Hub is only reachable from devices in your tailnet, and Agents on other machines connect to it over the tailnet as well. [Tailscale Serve](https://tailscale.com/kb/1312/serve) replaces the reverse proxy: it terminates TLS with a certificate for the MagicDNS name of the machine and forwards the requests to the Hub. + +## Prerequisites + +- A [Tailscale](https://tailscale.com) account with [MagicDNS](https://tailscale.com/kb/1081/magicdns) and [HTTPS certificates](https://tailscale.com/kb/1153/enabling-https) enabled in the admin console. HTTPS is required, because the login cookie is always marked as `Secure`. +- The Hub (and optionally an Agent) installed as described in the [installation guide](/docs/setup/installation). + + + The MagicDNS name of your machine, e.g. `orca.tail1234.ts.net`, will be published in the public + Certificate Transparency logs once a certificate is issued for it. The Hub itself stays private. + + +## Tailscale on the Host + +The easiest setup is to install Tailscale directly on the machine that runs the Hub. The Hub keeps listening on `127.0.0.1:8080` as in the default `docker-compose.yml`. + +### Install Tailscale [step] + +Install Tailscale on the host and connect it to your tailnet, see the [Tailscale install guide](https://tailscale.com/download/linux): + +```bash +curl -fsSL https://tailscale.com/install.sh | sh +sudo tailscale up --hostname=orca +``` + +The Hub will be reachable at `https://orca..ts.net`. You can find the name of your tailnet in the [DNS settings](https://login.tailscale.com/admin/dns) of the admin console. + +### Configure the Hub [step] + +Set `APP_URL` to the MagicDNS name of the machine. It has to match the URL in the browser exactly: + +```bash title=".env" +APP_URL=https://orca.tail1234.ts.net +``` + +Tailscale Serve sends the IP address of the client in the `X-Forwarded-For` header. The Hub only uses this header if the request comes from a trusted proxy. Requests from the host to the published port reach the Hub from the gateway of the Docker network, so add it to `TRUSTED_PROXIES`: + +```bash +docker network inspect orca_default --format '{{(index .IPAM.Config 0).Gateway}}' +``` + +Replace `orca_default` with the network of your Compose project (`docker network ls`), and use the output: + +```bash title=".env" +TRUSTED_PROXIES=172.18.0.1 +``` + +Recreate the Hub so it picks up the new values: + +```bash +docker compose up -d hub +``` + +### Start Tailscale Serve [step] + +Forward HTTPS traffic from the tailnet to the Hub: + +```bash +sudo tailscale serve --bg 8080 +``` + +The configuration is persistent and survives restarts. Check it with `tailscale serve status`. Open `https://orca..ts.net` from a device in your tailnet and continue with [creating an admin account](/docs/setup/installation#create-an-admin-account). + +## Tailscale as a Sidecar Container + +If you don't want to install Tailscale on the host, you can run it as a container next to the Hub. The Hub shares the network of the Tailscale container, so it is only reachable through the tailnet. + +Create an [auth key](https://tailscale.com/kb/1085/auth-keys) in the admin console and add a Tailscale service to your `docker-compose.yml`: + +```yml title="docker-compose.yml" lineNumbers +services: + tailscale: # [!code ++] + image: tailscale/tailscale:latest # [!code ++] + container_name: orca-tailscale # [!code ++] + hostname: orca # [!code ++] + restart: unless-stopped # [!code ++] + environment: # [!code ++] + - TS_AUTHKEY=tskey-auth-... # [!code ++] + - TS_STATE_DIR=/var/lib/tailscale # [!code ++] + - TS_SERVE_CONFIG=/config/serve.json # [!code ++] + volumes: # [!code ++] + - ./data/tailscale:/var/lib/tailscale # [!code ++] + - ./tailscale:/config # [!code ++] + + hub: + image: ghcr.io/orcacd/hub:latest + container_name: orca-hub + restart: unless-stopped + env_file: .env + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + network_mode: service:tailscale # [!code ++] + ports: # [!code --] + - "127.0.0.1:8080:8080" # [!code --] + volumes: + - ./data/hub:/app/data +``` + +Create `tailscale/serve.json` to forward HTTPS traffic to the Hub. Tailscale replaces `${TS_CERT_DOMAIN}` with the MagicDNS name of the container: + +```json title="tailscale/serve.json" lineNumbers +{ + "TCP": { + "443": { + "HTTPS": true + } + }, + "Web": { + "${TS_CERT_DOMAIN}:443": { + "Handlers": { + "/": { + "Proxy": "http://127.0.0.1:8080" + } + } + } + } +} +``` + +In this setup Tailscale Serve connects to the Hub via `127.0.0.1`, so set the `.env` values accordingly: + +```bash title=".env" +APP_URL=https://orca.tail1234.ts.net +TRUSTED_PROXIES=127.0.0.1 +``` + +If the Agent runs in the same Compose project, it can no longer reach the Hub as `hub`, because the Hub now uses the network of the Tailscale container. Set `HUB_URL=http://tailscale:8080` in its `agent.env` instead. The connection between Hub and Agent is [end-to-end encrypted](/docs/advanced/security#hub-and-agent-communication), even without TLS. + +## Connecting Agents + +An Agent on the same machine as the Hub doesn't need Tailscale. It can connect to the Hub through the Docker network, e.g. with `HUB_URL=http://hub:8080`. + +For Agents on other machines, install Tailscale on these machines as well and set `HUB_URL` to the MagicDNS name of the Hub: + +```bash title="agent.env" +HUB_URL=https://orca.tail1234.ts.net +``` + +Containers can reach the tailnet through Tailscale on the host, but they don't always use MagicDNS to resolve names. If the Agent can't resolve the name of the Hub, add it with its Tailscale IP (`tailscale ip -4` on the Hub machine) to the Agent service: + +```yml title="docker-compose.yml" +services: + agent: + extra_hosts: # [!code ++] + - "orca.tail1234.ts.net:100.101.102.103" # [!code ++] +``` + +## Restricting Access + +Everyone who can reach the Hub in your tailnet can open the login page. Use the [access control policy](https://tailscale.com/kb/1018/acls) of your tailnet to restrict access to the Hub to your admins and Agents, for example with tags: + +```json title="Tailnet policy file" lineNumbers +{ + "tagOwners": { + "tag:orca-hub": ["autogroup:admin"], + "tag:orca-agent": ["autogroup:admin"] + }, + "grants": [ + { + "src": ["autogroup:admin", "tag:orca-agent"], + "dst": ["tag:orca-hub"], + "ip": ["tcp:443"] + } + ] +} +``` + +Additionally, you can restrict the Hub to Tailscale IP addresses with `ALLOWED_IPS`: + +```bash title=".env" +ALLOWED_IPS=100.64.0.0/10,fd7a:115c:a1e0::/48 +``` + +## Webhooks and GitHub Actions + +Git providers, container registries and GitHub Actions can't reach a Hub that is only available in your tailnet. You can either rely on [polling](/docs/advanced/architecture/repositories#sync-strategies), or make the Hub publicly reachable with [Tailscale Funnel](https://tailscale.com/kb/1223/funnel) instead of Tailscale Serve: + +```bash +sudo tailscale funnel --bg 8080 +``` + +For the [sidecar container](#tailscale-as-a-sidecar-container), enable Funnel in `tailscale/serve.json` instead: + +```json title="tailscale/serve.json" +{ + "AllowFunnel": { // [!code ++] + "${TS_CERT_DOMAIN}:443": true // [!code ++] + }, // [!code ++] + "TCP": { ... }, + "Web": { ... } +} +``` + +With Funnel the whole Hub is public. Restrict the UI and API to your tailnet with `ALLOWED_IPS`, as shown above. `ALLOWED_IPS` does not apply to webhooks, GitHub Actions, the health check and Agent connections, so they keep working from the internet. Tailscale passes the real IP of public clients to the Hub, so they are blocked by `ALLOWED_IPS` and rate limiting works as expected. + + + Only use Funnel together with `ALLOWED_IPS` and follow the other recommendations on the [security + page](/docs/advanced/security), as the Hub is exposed to the internet. + + +## Services in your Tailnet + +The Hub blocks requests to private IP addresses, including the Tailscale range `100.64.0.0/10`, to prevent [SSRF attacks](/docs/advanced/security#outbound-requests-ssrf-protection). If your Git server, OIDC provider or notification service is only reachable in your tailnet, allow its Tailscale IP with `ALLOWED_INTERNAL_IPS`: + +```bash title=".env" +ALLOWED_INTERNAL_IPS=100.101.102.104 +``` + +Allow single IPs rather than the whole tailnet range. + +This requires that the Hub can reach the tailnet. With Tailscale on the host this works out of the box. The sidecar container uses userspace networking by default, which only handles incoming connections. To make outgoing connections work, switch it to kernel networking: + +```yml title="docker-compose.yml" +services: + tailscale: + environment: + - TS_USERSPACE=false # [!code ++] + devices: # [!code ++] + - /dev/net/tun:/dev/net/tun # [!code ++] + cap_add: # [!code ++] + - net_admin # [!code ++] +``` diff --git a/content/docs/configuration/env-variables.mdx b/content/docs/configuration/env-variables.mdx index d23682b..0f3c893 100644 --- a/content/docs/configuration/env-variables.mdx +++ b/content/docs/configuration/env-variables.mdx @@ -3,8 +3,7 @@ title: Environment Variables description: Complete reference for all OrcaCD configuration options --- -Below are all the environment variables supported by OrcaCD. These should be configured in your `.env` file. -Be cautious when modifying environment variables that are not recommended to change. +With the default `docker-compose.yml`, the Hub reads its variables from `.env` and the Agent from `agent.env`. Each variable is tagged with a **Type**: @@ -17,31 +16,39 @@ Each variable is tagged with a **Type**: These environment variables can be used in both the agent and the hub. -| Variable | Default Value | Type | Description | -| ----------- | ------------- | -------------------------------------- | ---------------------------------------------------------- | -| `LOG_LEVEL` | `info ` | Optional | Log level. Options: trace, debug, info, warn, error, fatal | -| `LOG_JSON` | `false` | Optional | Enable JSON formatted logs. | +| Variable | Default Value | Type | Description | +| ----------- | ------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `LOG_LEVEL` | `info` | Optional | Log level: `trace`, `debug`, `info`, `warn`, `error` or `fatal`. The setup code is logged as `warn`. | +| `LOG_JSON` | `false` | Optional | Log in JSON format. | ## Hub -| Variable | Default Value | Type | Description | -| ---------------------- | ------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `APP_URL` | `-` | Required | The URL of the OrcaCD instance | -| `APP_SECRET` | `-` | Required | Secret used for authentication and encryption. Generate one with `openssl rand -base64 42` | -| `TRUSTED_PROXIES` | `-` | Recommended | Recommended if using a reverse proxy. Comma seperated list of network origins | -| `DISABLE_LOCAL_AUTH` | `false` | Security | Disable password authentication. Useful when using SSO with OIDC | -| `ALLOWED_IPS` | `-` | Security | Harden the hub by only allowing requests from certain IPs. Comma seperated list of CIDR ranges or single IPs. This does not apply to webhooks or the agent connections. | -| `HOST` | `0.0.0.0` | Optional | Host address | -| `PORT` | `8080` | Optional | Container port | -| `DISABLE_UI` | `false` | Optional | Disable serving the frontend. Useful when using the hub only for API access. | -| `ALLOWED_INTERNAL_IPS` | `-` | Optional | Private IPs that should be ignored by the SSRF protection. Comma seperated list of CIDR ranges or single IPs | -| `DEMO` | `false` | Optional | This mode disables all functions except the login and additionally seeds some demo data in the database | +| Variable | Default Value | Type | Description | +| ---------------------- | ------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `APP_URL` | `-` | Required | Public URL of the Hub, e.g. `https://orcacd.example.com`, without a path. Must match the URL in the browser. Login requires HTTPS, except on `localhost`. | +| `APP_SECRET` | `-` | Required | Secret for authentication and database encryption, at least 32 characters. Generate one with `openssl rand -base64 42`. See [`hub key rotate`](/docs/advanced/cli#key-rotate) to change it. | +| `TRUSTED_PROXIES` | `-` | Recommended | Comma separated IPs or CIDR ranges of your reverse proxies. | +| `DISABLE_LOCAL_AUTH` | `false` | Security | Disable password login, e.g. when using [OIDC](/docs/guides/oidc). | +| `ALLOWED_IPS` | `-` | Security | Comma separated IPs or CIDR ranges that can access the Hub. Webhooks, GitHub Actions, the health check and Agent connections are always allowed. | +| `HOST` | `-` | Optional | Address the Hub listens on. Defaults to all interfaces. | +| `PORT` | `8080` | Optional | Port the Hub listens on. | +| `DISABLE_UI` | `false` | Optional | Don't serve the web UI, e.g. for API-only use. | +| `ALLOWED_INTERNAL_IPS` | `-` | Optional | Comma separated private IPs or CIDR ranges the Hub may connect to, e.g. a self-hosted Git server or OIDC provider. | +| `DEMO` | `false` | Optional | Demo mode: seeds demo data and disables everything except the login. | ## Agent -| Variable | Default Value | Type | Description | -| ------------------------------------- | ------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -| `HUB_URL` | `-` | Required | The URL of the Hub. Example: `https://example.com` | -| `AUTH_TOKEN` | `- ` | Required | The authentication token of the agent. Register an agent in the hub frontend to retrieve it | -| `RESTRICT_VOLUMES_TO_DEPLOYMENTS_DIR` | `false` | Security | Restrict the volumes that can be mounted to locations within the OrcaCD deployments directory. This enhances security. | -| `ALLOWED_PRIVILEGED_APPS` | `-` | Optional | Comma seperated list of app id that are allowed to bypass certain restrictions enforced by OrcaCD | + + Deployed Compose files can read the Agent's environment. Never give the Agent the `.env` file of + the Hub. + + +| Variable | Default Value | Type | Description | +| ------------------------------------- | -------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `HUB_URL` | `-` | Required | URL of the Hub, usually the same as `APP_URL`. | +| `AUTH_TOKEN` | `-` | Required | Token of the Agent, created when you add the Agent in the Hub. | +| `RESTRICT_VOLUMES_TO_DEPLOYMENTS_DIR` | `false` | Security | Only allow host paths inside the deployment directory of the application. See [deployment policy](/docs/advanced/security#compose-deployment-policy). | +| `ALLOWED_PRIVILEGED_APPS` | `-` | Optional | Comma separated application IDs that skip the [deployment policy](/docs/advanced/security#compose-deployment-policy) entirely. | +| `CA_CERT_FILE` | `-` | Optional | Path to a PEM CA certificate inside the Agent container, for Hubs with a self-signed or private CA certificate. | +| `HEALTHCHECK_PORT` | `8090` | Optional | Port of the internal health check endpoint on `127.0.0.1`. | +| `DEPLOYMENTS_DIR` | `/deployments` | Optional | Directory inside the Agent container where deployments are stored. Must be mounted from the host. | diff --git a/content/docs/configuration/webhooks.mdx b/content/docs/configuration/webhooks.mdx index 237bade..6023dd6 100644 --- a/content/docs/configuration/webhooks.mdx +++ b/content/docs/configuration/webhooks.mdx @@ -18,6 +18,7 @@ This webhook endpoint triggers a check of the Git repository for changed Compose For setting up webhooks for your Git provider, you need the webhook URL and secret generated by OrcaCD when you add a repository with sync type **Webhook**. In case you have already added the repository, change the sync type to **Webhook** in the repository settings to generate the webhook URL and secret. +The secret is only shown once, so make sure to copy it right away. The webhook URL has the format `https://orcacd.example.com/api/v1/webhooks/repositories/`. ### GitHub @@ -32,32 +33,36 @@ Go to the Webhooks settings page of your repository. Paste the webhook URL and s ### Generic -You can also easily call the webhook endpoint from any system by sending a POST request to the webhook URL with the secret in the `Authorization` header. +You can also easily call the webhook endpoint from any system by sending a POST request to the webhook URL with the secret in the `Authorization` header (either the raw secret or `Bearer `). You can optionally include a JSON body with the following fields, but it's not required: ```json { - // Optional body fields "ref": "refs/heads/main", "branch": "main", "commit": "abc123" } ``` +If both `ref` and `branch` are set, `ref` takes precedence. Without a branch, all applications of the repository are synced. Without a commit, OrcaCD fetches the latest commit from the Git provider. + ## Container Registries This endpoint will trigger a image pull and container recreation if the image digest has changed. This can be used for mutable tags like `latest` or `stable` to ensure that the latest image is always deployed, even without adjusting the tag in the Compose file. -The webhook URL and secret can be generated in OrcaCD by clicking on Applications -> Your Application -> Image-Polling (Settings). -Click on generate webhook and the secret will be displayed. Copy it and afterwards you will also see the webhook URL. -You can re-generate the secret at any time or disable the webhook if you no longer want to use it. +The webhook URL and secret can be generated in OrcaCD by clicking on Applications → Your Application → Settings → **Image Updates**. +Click on **Generate Webhook** in the **Image Pull Webhook** section and the secret will be displayed. Copy it, as it will not be shown again. Afterwards you will also see the webhook URL. +You can create a new secret at any time with **Regenerate** or remove the webhook with **Revoke** if you no longer want to use it. + +Webhook deliveries for the same application that arrive within a few seconds of each other are combined into a single image pull, so one push that triggers several webhooks only results in one deployment. ### GitHub Packages Go to the Settings → Webhooks → Add webhook page of your repository linked to your Package (Container). This webhooks is only triggered for packages that are linked to the selected repository. Paste the webhook URL and secret from OrcaCD and select the `application/json` content type option. -Then select `Let me select individual events.` and make sure that only the `Registry packages` event is selected. +Then select `Let me select individual events.` and make sure that only the **Packages** event is selected. Other events, including **Registry packages**, are rejected by OrcaCD. +Only published or updated container images trigger an image pull. Deliveries for untagged manifests and signature or attestation tags (e.g. `sha256-.sig`) are ignored. ### Docker Hub @@ -79,4 +84,17 @@ It's also strongly recommended to enable TLS verification for the webhook. Click ### Generic -Send a HTTP POST request to the webhook URL with the secret in the `Authorization` header and without any body. +Send a HTTP POST request to the webhook URL with the secret in the `Authorization` header (either the raw secret or `Bearer `). No body is required. + +## Responses and Rate Limits + +| Status | Meaning | +| ------ | ---------------------------------------------------------------------------------------- | +| `204` | The webhook was accepted (or intentionally ignored, e.g. a non-push event) | +| `400` | The payload is invalid or the event type is not supported | +| `401` | Unknown ID, invalid secret or signature, or the webhook is not enabled for this resource | +| `429` | Too many requests | + +To not reveal whether a repository or application exists, OrcaCD returns the same `401` response for unknown IDs, wrong secrets and resources without an enabled webhook. If your webhook fails with `401`, check the URL, the secret and that the repository sync type is set to **Webhook** (or that the image pull webhook is generated for the application). + +All webhook endpoints and the [GitHub Actions](../guides/github-actions) endpoint share a rate limit of 1 request per second per IP address with a burst of 10 requests. diff --git a/content/docs/demo.mdx b/content/docs/demo.mdx index e15f2cc..c3aab8c 100644 --- a/content/docs/demo.mdx +++ b/content/docs/demo.mdx @@ -1,17 +1,17 @@ --- title: Demo -description: Live demo of Orca CD in action, showcasing its features and capabilities. +description: Live demo of OrcaCD in action, showcasing its features and capabilities. --- The demo instance is currently not available. Please check back later for updates. -To access the live demo of Orca CD, please visit the following link: +To access the live demo of OrcaCD, please visit the following link: -[Live Demo](https://demo.orcacd.com) +[Live Demo](https://demo.orcacd.dev) The credentials for the demo are as follows: -- Username: `demo@orcacd.dev` +- Email: `demo@orcacd.dev` - Password: `demo-password` diff --git a/content/docs/guides/github-actions.mdx b/content/docs/guides/github-actions.mdx index 363d1c8..531f4dd 100644 --- a/content/docs/guides/github-actions.mdx +++ b/content/docs/guides/github-actions.mdx @@ -8,32 +8,49 @@ OrcaCD supports triggering deployments directly from GitHub Actions using [GitHu ## Prerequisites - A GitHub repository connected in OrcaCD with provider type **GitHub** +- **GitHub Actions Access** enabled in the sync settings of the repository in OrcaCD - The OrcaCD Hub must be reachable from GitHub's network. ## Example Workflow -Add a step at the end of your CI workflow to notify OrcaCD after a successful build or image push: +Add a step at the end of your CI workflow to notify OrcaCD after a successful build or image push. The job needs the `id-token: write` permission to request an OIDC token: ```yaml -permissions: - id-token: write - -- uses: OrcaCD/deploy-action@... # Replace ... with the latest version or hash - with: - # The OrcaCD hub URL (required) - # Required - hub: https://orca.example.com - - # Whether to sync the repo content (compose files) - # Optional, default: true - syncRepo: true - - # Whether to pull the latest container image versions, - # even it the compose file has not changed - # Optional, default: false - pullImages: false +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: OrcaCD/deploy-action@... # Replace ... with the latest version or hash + with: + # The OrcaCD hub URL, must match the APP_URL of the hub + # Required + hub: https://orca.example.com + + # Whether to sync the repo content (compose files) + # Optional, default: false + syncRepo: true + + # Whether to pull the latest container image versions, + # even if the compose file has not changed + # Optional, default: false + pullImages: false ``` +At least one of `syncRepo` or `pullImages` must be set to `true`, otherwise the action fails. + +## Restricting Access + +In the sync settings of the repository you can further restrict what GitHub Actions is allowed to do: + +- **Allowed actions**: Allow repository sync, image sync or both. +- **Allowed branches**: Only allow deployments from the listed branches (case-sensitive, exact match). Leave empty to allow all branches. +- **Allowed workflows**: Only allow the listed workflow files, e.g. `deploy.yml` (case-sensitive). Leave empty to allow all workflows. + +Requests that violate these settings are rejected with `403 Forbidden`. + ## Use Cases - Trigger on tag creation to deploy a new version of your application when a new release is created @@ -46,4 +63,4 @@ When a GitHub Actions workflow runs, it requests a short-lived OIDC token from G 1. The token's repository claim matches a GitHub repository registered in OrcaCD with GitHub Actions OIDC enabled. 2. The workflow was **not** triggered by a `pull_request` or `pull_request_target` event (to prevent untrusted forks from triggering deployments). -If verification succeeds, OrcaCD finds all applications linked to the matching repository and branch, then dispatches the requested actions. +If verification succeeds, OrcaCD finds all applications linked to the matching repository and branch, then dispatches the requested actions. For runs triggered by a tag, OrcaCD uses all branches that contain the tagged commit. With `syncRepo`, the exact commit of the workflow run is deployed, not the latest commit of the branch. diff --git a/content/docs/guides/oidc.mdx b/content/docs/guides/oidc.mdx index 282ac9a..b2e97a2 100644 --- a/content/docs/guides/oidc.mdx +++ b/content/docs/guides/oidc.mdx @@ -1,6 +1,6 @@ --- title: OIDC Authentication -description: Learn how to use OIDC providers for auhentication in OrcaCD +description: Learn how to use OIDC providers for authentication in OrcaCD --- OrcaCD supports OIDC authentication, allowing users to log in using their existing accounts from various OIDC providers. This guide will walk you through the process of setting up OIDC authentication in OrcaCD. @@ -9,9 +9,9 @@ OrcaCD supports OIDC authentication, allowing users to log in using their existi When using OIDC authentication, you can disable password login to enhance security. This ensures that users can only log in using their OIDC provider accounts, reducing the risk of unauthorized access through weak or compromised passwords. Before disabling password login, make sure the OIDC - authentication is properly configured and tested. + authentication is properly configured and tested, and that at least one admin can log in via SSO. - To disable password login, set the `DISABLE_LOCAL_AUTH` environment variable. Look at the [environment variables documentation](/docs/configuration/env-variables) for more details. + To disable password login, set the `DISABLE_LOCAL_AUTH` environment variable. Look at the [environment variables documentation](/docs/configuration/env-variables) for more details. If you lock yourself out, see [Account Recovery](/docs/troubleshooting/account-recovery). @@ -21,7 +21,7 @@ To set up OIDC authentication with Pocket ID, follow these steps: ### 1. Create a Pocket ID Application -Go to your Pocket ID dashboard and create a new application. Give it a name and leave the redirect URI blank for now. +Go to your Pocket ID dashboard and create a new application. Give it a name and leave the callback URL blank for now. You will get it from OrcaCD in step 3. ![Pocket ID Application Creation](/assets/docs/pocketid-orca-registration.png?url) @@ -29,21 +29,48 @@ You can find the OrcaCD logo [here](https://github.com/OrcaCD/orca-cd/tree/main/ ### 2. Configure OrcaCD -1. Go to the OrcaCD Admin settings and navigate to the OIDC configuration section. -2. Create a new OIDC provider configuration with the following details: - - **Name**: A name for your OIDC provider (e.g., "Pocket ID"). - - **Issuer URL**: The issuer URL provided by Pocket ID (e.g., `https://example.com`). - - **Client ID**: The client ID from your Pocket ID application. - - **Client Secret**: The client secret from your Pocket ID application. -3. Save the configuration and ensure it is enabled. +1. Go to the OrcaCD Admin settings, open the **SSO Providers** section and click **Add Provider**. +2. Enter a display name for the login button (e.g., "Pocket ID") and the issuer URL, client ID and client secret from your Pocket ID application. +3. Click **Create Provider**. The Hub validates the issuer URL before saving, see [provider settings](#provider-settings). ![OrcaCD OIDC Configuration](/assets/docs/orca-sso-registration.png?url) -### 3. Test the Configuration +### 3. Set the Callback URL + +After saving, the provider list shows the **Callback URL** of the provider. It has the format `/api/v1/auth/oidc//callback`. Copy it with the copy button and add it as callback URL to your Pocket ID application. + +### 4. Test the Configuration 1. Log out of OrcaCD if you are currently logged in. -2. Click on the "Login with Pocket ID" button on the login page. -3. You will be redirected to the Pocket Id login page. Enter your credentials and log in. +2. Click on the "Continue with Pocket ID" button on the login page. +3. You will be redirected to the Pocket ID login page. Enter your credentials and log in. 4. After successful authentication, you will be redirected back to OrcaCD and logged in with your Pocket ID account.
![OrcaCD Login Page](/assets/docs/sso-login-screen.png?url)
+ +## Provider settings + +When a provider is saved, the Hub loads its discovery document (`/.well-known/openid-configuration`). If that fails, the provider is not saved. The client secret is stored encrypted; leave it blank when editing to keep the current one. + +The following options are enabled by default: + +- **Enabled**: Disabled providers are not shown on the login page. Disabling or deleting a provider signs out all users linked to it. +- **Require verified emails**: Logins are only accepted if the provider confirms the email address with `email_verified=true`. +- **Allow auto signup**: New OrcaCD users are created on their first login. If disabled, only existing users can log in. + +### Self-hosted providers in a private network + +The Hub talks to the provider directly to load the discovery document, exchange the login code and fetch user information. These requests are protected against SSRF, so issuers that resolve to a private IP address (e.g. `192.168.x.x` or `10.x.x.x`) are rejected and the provider can not be saved. Add the IP or range of your provider to the `ALLOWED_INTERNAL_IPS` environment variable of the Hub to allow it. See the [environment variables documentation](/docs/configuration/env-variables). + +## How SSO users are handled + +- **Login flow**: OrcaCD uses the authorization code flow with PKCE (`S256`), a state parameter and a nonce. +- **User information**: The email address, name and profile picture are read from the ID token. If the ID token does not contain the email address or `email_verified`, the Hub fetches them from the userinfo endpoint of the provider. An email address is required to log in. +- **Account linking**: On the first login with a provider, OrcaCD looks for an existing user with the same email address and links the SSO login to it. This also works for accounts that were created with a password, including the admin account. Accounts whose users changed the email address themselves are not linked, because OrcaCD does not verify these addresses. Such logins fail with "Your account could not be linked. Please contact an administrator." An admin can resolve this by changing the email address of the account in **User Management**. Only a change by an admin clears the unverified state; saving the same address again does not. If no user exists, a new one is created (see "Allow auto signup"). Later logins are matched by the provider's user ID, and the name and email address are updated from the provider on every login. +- **Roles**: New SSO users get the role `User`. Groups or roles from the provider are not mapped. Admins can promote users in the **User Management** section of the Admin settings. + + + Because SSO logins are linked to existing accounts by email address, only disable "Require + verified emails" if your provider makes sure that users can not use email addresses they do not + own. Otherwise, someone could log in as an existing user, including an admin. + diff --git a/content/docs/guides/reverse-proxy.mdx b/content/docs/guides/reverse-proxy.mdx index c0e56dd..ef0b1ee 100644 --- a/content/docs/guides/reverse-proxy.mdx +++ b/content/docs/guides/reverse-proxy.mdx @@ -6,18 +6,23 @@ description: Learn how to use a reverse proxy with OrcaCD In a production environment, it is strongly recommended to run the OrcaCD Hub behind a reverse proxy with a domain and a valid TLS certificate. - Make sure to adjust the `TRUSTED_PROXIES` environment variable to include the IP address of your - reverse proxy, otherwise the Hub will not be able to determine the correct client IP address. You - will also need to set the public URL in the hub and agent environment variables. + Make sure to adjust the `TRUSTED_PROXIES` environment variable to include the IP address (or CIDR + range) of your reverse proxy, otherwise the Hub will not be able to determine the correct client + IP address. You will also need to set the public URL in the hub and agent environment variables. ## Nginx To use Nginx as a reverse proxy for OrcaCD, expose the Hub on localhost only and forward traffic from your public domain to the Hub port. -Use this `server` block in your Nginx config: +Use this `map` and `server` block in your Nginx config (the `map` block belongs into the `http` context): ```nginx lineNumbers +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + server { listen 80; server_name orcacd.example.com; @@ -36,7 +41,7 @@ server { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - # Prevent Websocket being closed by Nginx + # Prevent the Agent WebSocket and the live updates (SSE) from being closed by Nginx proxy_send_timeout 120s; proxy_read_timeout 120s; } @@ -62,13 +67,17 @@ services: hub: image: ghcr.io/orcacd/hub:latest container_name: orca-hub - ports: - - "127.0.0.1:8080:8080" restart: unless-stopped env_file: .env - labels: // [!code ++] - - "traefik.enable=true" // [!code ++] - - "traefik.http.routers.orcacd.rule=Host(`orcacd.example.com`)" // [!code ++] + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + ports: + - "127.0.0.1:8080:8080" + labels: # [!code ++] + - "traefik.enable=true" # [!code ++] + - "traefik.http.routers.orcacd.rule=Host(`orcacd.example.com`)" # [!code ++] volumes: - ./data/hub:/app/data ``` diff --git a/content/docs/helping-out/contributing.mdx b/content/docs/helping-out/contributing.mdx index 61ba795..4d46d8f 100644 --- a/content/docs/helping-out/contributing.mdx +++ b/content/docs/helping-out/contributing.mdx @@ -26,21 +26,90 @@ We use Go and Node.js for the development of the backend and frontend respective ### Setting up the development environment 1. Install all required tools listed above. -2. Clone the repository and navigate to the project directory. -3. For the frontend, navigate to the `frontend/` directory and run `npm ci` to install dependencies. -4. Run the frontend development server with `node --run dev` from the `frontend/` directory. -5. Run the backend services with `just run-hub` and `just run-agent` in the `backend/` directory. -6. Access the app at `http://localhost:3000` and the hub API at `http://localhost:8080`. +2. Clone the [repository](https://github.com/OrcaCD/orca-cd) and navigate to the project directory. +3. Install the frontend dependencies: + + ```sh + cd frontend + pnpm i --frozen-lockfile + ``` + +4. Copy `backend/.env.dev.example` to `backend/.env` and set `APP_SECRET` to a random value with at least 32 characters. The `Justfile` loads this file automatically. + + ```sh + cp backend/.env.dev.example backend/.env + ``` + +5. Start the Hub and the frontend development server from the repository root: + + ```sh + just dev + ``` + +6. Open the app at `http://localhost:3000`. The Hub API runs on `http://localhost:8080`, and the Vite dev server proxies `/api` requests to it. + +To run an Agent locally, [register an agent](/docs/advanced/architecture/agents) in the UI, set `AUTH_TOKEN` in `backend/.env` and start it with `just run-agent` (or use `just dev-all` to start the Hub, Agent and frontend together). + +### Just commands + +The `Justfile` in the repository root includes all backend commands, so they can be run from the root or from the `backend/` directory. To list all available commands with a short description, run: + +```sh +just -l +``` + +Backend tests need `CGO_ENABLED=1`. The `just` recipes set it for you. If you run `go test` directly, set it yourself: + +```sh +CGO_ENABLED=1 go test -v -race -run TestName ./internal/hub/routes/ +``` + +### Linting, formatting and type checking + +Run these commands from the `frontend/` directory (and from `e2e/` if you changed the E2E tests): + +```sh +node --run lint # oxlint +node --run format # oxfmt (use format:check to only verify) +node --run typecheck # TypeScript type check +node --run translations:check # check for unused and missing message keys (frontend only) +``` + +For the backend, use `just lint`, `just fmt` and `just test`. + +### Translations + +The frontend uses Paraglide (inlang) for translations. Messages are stored in `frontend/messages/.json`, with `en.json` as the source file. When you add a new message key, add it to every locale file. Remove messages you no longer use, because `node --run translations:check` fails on unused messages and on keys that are missing in `en.json`. All other translations are managed on Crowdin, see [Translating](/docs/helping-out/translating). + +### End-to-end tests + +The E2E tests use [Playwright](https://playwright.dev) and live in the `e2e/` directory. Install the dependencies and browsers first: + +```sh +cd e2e +pnpm i --frozen-lockfile && pnpm run install:browsers +``` + +Then run the tests: + +```sh +pnpm test # run all tests headless +pnpm test:ui # run the tests in the interactive Playwright UI +``` + +- Playwright builds the Hub and Agent images and starts the Hub with `docker-compose.e2e.yml` on `http://127.0.0.1:8090`. Set `E2E_SKIP_BUILD=1` to skip the image build if the images are already built. +- External services are mocked by small Node.js servers in `e2e/mock/`: `gitea` (Git provider), `oidc` (OIDC provider) and `receiver` (notification targets). The `certs` folder contains the self-signed certificate of the Gitea mock, which the Hub is configured to trust. +- The agent, application and image tests start an Agent container through Docker Compose and run after all other tests. They need access to the Docker socket. ### Running locally with Docker A Docker Compose file is provided for running the full stack locally: ```sh -docker compose -f docker-compose.dev.yaml up --build +docker compose -f docker-compose.dev.yml up --build ``` -The hub service will be available at `http://localhost:8080`. +The Hub will be available at `http://localhost:8080`. The Agent needs an `AUTH_TOKEN`, so [register an agent](/docs/advanced/architecture/agents) in the UI and set it in `docker-compose.dev.yml`. To test OIDC login, start a local [Pocket ID](https://pocket-id.org) instance on `http://localhost:1411` with `--profile pocket-id`. ### Submitting a Pull Request @@ -56,11 +125,15 @@ Before you submit the pull request for review please ensure that fix: hide global audit log switch for non admin users ``` - Where `TYPE` can be: - - feat - is a new feature - - doc - documentation only changes + Where `type` can be: + - feat - a new feature - fix - a bug fix - - refactor - code change that neither fixes a bug nor adds a feature + - docs - documentation only changes + - refactor - a code change that neither fixes a bug nor adds a feature + - test - adding or updating tests + - ci - changes to the CI workflows + - chore - maintenance such as dependency updates - Your pull request has a detailed description - You have added tests for any new features or bug fixes +- Linting, formatting, type checks and tests pass diff --git a/content/docs/helping-out/documentation.mdx b/content/docs/helping-out/documentation.mdx index dca7d1f..3a99307 100644 --- a/content/docs/helping-out/documentation.mdx +++ b/content/docs/helping-out/documentation.mdx @@ -3,16 +3,18 @@ title: Documentation description: Contribute to the OrcaCD website or documentation --- -This guide explains how to add or edit the OrcaCD documentation. The source code of the documentation is stored in the [OracaCD/docs GitHub repository](https://github.com/OrcaCD/docs). +This guide explains how to add or edit the OrcaCD documentation. The source code of the documentation is stored in the [OrcaCD/docs GitHub repository](https://github.com/OrcaCD/docs). ## 1. Where docs live -All markdown pages are under `/content/docs`. The file `meta.json` controls the sidebar structure and order. +All pages are MDX files under `/content/docs`. The file `/content/docs/meta.json` controls the sidebar structure and order. A section folder can have its own `meta.json` to define the order of its pages (e.g. `/content/docs/guides/meta.json`). + +To run the site locally, install the dependencies with `pnpm install` and start the development server with `pnpm run dev`. ## 2. Adding a new page 1. Pick the correct folder that matches a collection pattern (e.g. `/guides`). -2. Create `/content/docs/
/.md`. +2. Create `/content/docs/
/.mdx`. 3. Add the frontmatter code: ```yaml lineNumbers @@ -22,15 +24,25 @@ description: Get OrcaCD running quickly with Docker installation --- ``` -4. Run `npm run dev` and check your page at `http://localhost:3000/docs/
/`. +4. Run `pnpm run dev` and check your page at `http://localhost:3000/docs/
/`. +5. If the section has its own `meta.json`, add the new page to its `pages` list. - To use components in the documentation please look at the [Fumadadocs Documentation for + To use components in the documentation please look at the [Fumadocs Documentation for Markdown](https://www.fumadocs.dev/docs/markdown) ## 3. Submitting changes -After making changes you can create a pull request to the `main` branch. Please make sure to commit using Conventional Commits (`docs: update guides`). +Before opening a pull request, format and check your changes: + +```sh +pnpm run format # format with oxfmt +pnpm run lint # lint with oxlint +pnpm run types:check # type check +pnpm run build # make sure the site builds +``` + +After that you can create a pull request to the `main` branch. Please make sure to commit using Conventional Commits (`docs: update guides`). Thanks for helping out! diff --git a/content/docs/helping-out/translating.mdx b/content/docs/helping-out/translating.mdx index 73fed3a..9106655 100644 --- a/content/docs/helping-out/translating.mdx +++ b/content/docs/helping-out/translating.mdx @@ -16,6 +16,8 @@ Please use informal language for translations if appropriate. 3. Open the `en.json` source file. 4. Now you can start translating the strings. +Translations are synced back to the repository automatically. A nightly GitHub Action downloads them from Crowdin and opens a pull request from the `i18n_crowdin` branch. + ## Adding a New Language Is your language missing from Crowdin? You can [request its addition](https://github.com/OrcaCD/orca-cd/issues/new?assignees=&labels=language-request&projects=&template=language-request.yml&title=%F0%9F%8C%90+Language+request%3A+%3Clanguage+name+in+english%3E). diff --git a/content/docs/index.mdx b/content/docs/index.mdx index e08826f..3114c74 100644 --- a/content/docs/index.mdx +++ b/content/docs/index.mdx @@ -40,14 +40,12 @@ The structure, architecture and concepts of OrcaCD can be found in the [architec ![Repositories Page](/assets/docs/repositories-page.png?url) -import { HomeIcon } from "lucide-react"; - Quickstart installation - Learn more the configuration options. + Learn more about the configuration options. diff --git a/content/docs/setup/installation.mdx b/content/docs/setup/installation.mdx index 345fa7d..cca252f 100644 --- a/content/docs/setup/installation.mdx +++ b/content/docs/setup/installation.mdx @@ -19,31 +19,42 @@ Before you start, make sure you have: - For a publicly reachable setup: a domain with a DNS record pointing to your server, and a reverse proxy which also handles TLS. By default the Hub only listens on `127.0.0.1:8080`, and you put your own reverse proxy (Nginx, Caddy, Traefik, ...) in front of it. See the [reverse proxy guide](../guides/reverse-proxy). - Just testing OrcaCD locally? You can skip the domain and reverse proxy setup, set the - `APP_URL=http://localhost:8080` in the `.env` file, and access the Hub at `http://localhost:8080`. + Just testing OrcaCD locally? You can skip the domain and reverse proxy setup, set + `APP_URL=http://localhost:8080` in the `.env` file and `HUB_URL=http://hub:8080` in the + `agent.env` file, and access the Hub at `http://localhost:8080`. ## Start Hub and Agent [step] -Download the [`docker-compose.yml`](https://raw.githubusercontent.com/OrcaCD/orca-cd/main/docker-compose.yml) and [`.env`](https://raw.githubusercontent.com/OrcaCD/orca-cd/main/.env.example) file: +Download the [`docker-compose.yml`](https://raw.githubusercontent.com/OrcaCD/orca-cd/main/docker-compose.yml), the [`.env`](https://raw.githubusercontent.com/OrcaCD/orca-cd/main/.env.example) file for the Hub and the [`agent.env`](https://raw.githubusercontent.com/OrcaCD/orca-cd/main/agent.env.example) file for the Agent: ```bash tab="curl" curl -o docker-compose.yml https://raw.githubusercontent.com/OrcaCD/orca-cd/main/docker-compose.yml curl -o .env https://raw.githubusercontent.com/OrcaCD/orca-cd/main/.env.example +curl -o agent.env https://raw.githubusercontent.com/OrcaCD/orca-cd/main/agent.env.example ``` ```bash tab="wget" wget -O docker-compose.yml https://raw.githubusercontent.com/OrcaCD/orca-cd/main/docker-compose.yml wget -O .env https://raw.githubusercontent.com/OrcaCD/orca-cd/main/.env.example +wget -O agent.env https://raw.githubusercontent.com/OrcaCD/orca-cd/main/agent.env.example ``` -Edit the `.env` file according to the instructions in the file. The most important values are: +Edit both files according to the instructions in them. The most important values in the `.env` file of the Hub are: - `APP_URL` - the URL under which the Hub will be reachable. OrcaCD does not provision this URL for you: it is your domain pointing at your reverse proxy (e.g. `https://orcacd.example.com`). - `APP_SECRET` - used for authentication and database encryption. Generate one with `openssl rand -base64 42`. + +In the `agent.env` file of the Agent: + - `HUB_URL` - the URL the Agent uses to reach the Hub, usually the same as `APP_URL`. - `AUTH_TOKEN` - leave empty for now; you will create it in the Hub UI in a later step. + + Do not put `APP_SECRET` into the `agent.env` file or give the Agent the `.env` file of the Hub. + Compose files deployed by the Agent can read the Agent's environment. + + We recommend customizing other environment variables as needed. See the [environment variables page](../configuration/env-variables) for more details. @@ -64,16 +75,28 @@ By default the hub only listens on port `8080` on localhost. Configure your reve ## Create an Admin Account [step] -Open the Hub in your browser at the `APP_URL` you configured (e.g. `https://orcacd.example.com/login`) and create an admin account. +As long as no user exists, the Hub prints a one-time setup code to its logs on every start. You need this code to create the first admin account: + +```bash +docker compose logs hub | grep setup-code +``` + +The log entry also contains a link like `https://orcacd.example.com/register#setup-code=XXXX-XXXX-XXXX-XXXX`. Open it to get the code filled in automatically, or open `https://orcacd.example.com/register` and enter the code manually. The password must be at least 12 characters long and include an uppercase letter, a lowercase letter, a number and a special character. ![Setup Admin Account](/assets/docs/account-setup.png?url) ## Connect your first Agent [step] -Navigate to the Agents page and click "Add Agent". Follow the instructions and copy the token and add it as `AUTH_TOKEN` to the `.env` file of your Agent deployment and restart the Agent. +Navigate to the Agents page, click "Add Agent" and enter a name. The setup dialog shows the generated token and a `compose.yml` for the Agent. As the Agent from this quickstart is already part of your `docker-compose.yml`, select **Copy agent auth token** in the menu next to **Copy** and add the token as `AUTH_TOKEN` to the `agent.env` file. Then recreate the Agent container so it picks up the new value (a plain `docker compose restart` does not reload the env file): + +```bash +docker compose up -d agent +``` ![Setup Agent](/assets/docs/create-agent.png?url) +The dialog waits for the first connection and switches to **Connected** as soon as the Agent is online. The token is only shown once. + ## Start Deploying [step] Add your first repository and create your first deployment. Connect a [OIDC provider](../guides/oidc) to allow your team to log in or invite them on the admin page. diff --git a/content/docs/setup/upgrading.mdx b/content/docs/setup/upgrading.mdx index c15c203..d0f3839 100644 --- a/content/docs/setup/upgrading.mdx +++ b/content/docs/setup/upgrading.mdx @@ -11,15 +11,22 @@ description: Keep your OrcaCD installation up to date To create a backup of your database, you can run the following command: ```bash - docker compose exec hub /app/hub backup + docker compose exec hub /app/hub export ``` - After running this command make sure to copy the created backup file and include it in your backup strategy. + The backup is written to `./data/hub/hub-export-YYYYMMDD-HHMMSS.db` on the host. Make sure to include this file in your backup strategy. See the [CLI reference](../advanced/cli#export) for more details. -For upgrading OrcaCD when using Docker, you just need to pull the latest image and restart the services: +Before upgrading, check the [changelog](https://github.com/OrcaCD/orca-cd/blob/main/CHANGELOG.md) for breaking changes. + +If you pinned the image versions in your `docker-compose.yml` (recommended), update the tags of the `hub` and `agent` images first. Then pull the new images and recreate the containers: ```bash docker compose pull && docker compose up -d ``` + + + Always upgrade the Hub and all Agents to the same version. Changes to the encrypted connection + between Hub and Agent can prevent older Agents from connecting to a newer Hub and vice versa. + diff --git a/content/docs/troubleshooting/account-recovery.mdx b/content/docs/troubleshooting/account-recovery.mdx index 95070f2..2d888b4 100644 --- a/content/docs/troubleshooting/account-recovery.mdx +++ b/content/docs/troubleshooting/account-recovery.mdx @@ -5,13 +5,24 @@ description: Steps to recover your account if you have lost access There are two ways to reset a password for a user: -1. UI: An admin can reset the password for a user in the admin panel under the "Users" tab by clicking on the three dots next to the user's name and selecting "Edit". -2. Terminal: You can reset a password for a user by running `hub reset-password `. To execute this script with Docker you have to run the following command: +1. UI: An admin can reset the password for a user in the admin panel under the "User Management" tab by clicking on the three dots next to the user's name and selecting "Edit". +2. Terminal: You can reset a password for a user by running `hub reset-password --email ` or `hub reset-password --id `. To execute this command with Docker you have to run the following command: ```bash -docker compose exec hub /app/hub reset-password +docker compose exec hub /app/hub reset-password --email ``` -The user with the reset password has to change the password on the next login. + + The user with the reset password has to change the password on the next login. Resetting a + password also signs the user out on all devices. + + + + Passwords can only be reset for users with a local account. Users that only log in via SSO have to + recover their account at the identity provider. If `DISABLE_LOCAL_AUTH=true` is set, password + login is disabled and a reset password can not be used until the variable is removed. + + +If no user exists yet and you lost the setup code for the first admin account, restart the Hub. A new setup code is printed to the logs on every start (`docker compose logs hub`). diff --git a/content/docs/troubleshooting/common-issues.mdx b/content/docs/troubleshooting/common-issues.mdx index e43a382..886b1d0 100644 --- a/content/docs/troubleshooting/common-issues.mdx +++ b/content/docs/troubleshooting/common-issues.mdx @@ -3,16 +3,37 @@ title: Common Issues description: Solutions to frequently encountered problems --- +## Setup code is missing in the Hub logs + +**Symptoms:** `docker compose logs hub` does not show a setup code for the first admin account. + +**Checks:** + +1. The setup code is only printed as long as no user exists. If an account was already created, log in or [recover the account](/docs/troubleshooting/account-recovery). +2. The setup code is logged with the `warn` level. Make sure `LOG_LEVEL` is not set to `error` or `fatal`. +3. A new setup code is generated on every start of the Hub. Always use the code from the latest start. + ## Agent stays Offline after connecting **Symptoms:** The Agent is running but shows as **Offline** in the Hub UI. **Checks:** -1. Verify `HUB_URL` in the Agent's `.env` is correct and reachable from the Agent's host. +1. Verify `HUB_URL` in the Agent's `agent.env` is correct and reachable from the Agent's host. 2. Confirm the `AUTH_TOKEN` matches the token generated for that Agent in the Hub UI. 3. Check Agent logs: `docker compose logs agent` and look for connection errors. 4. If the Hub is behind a reverse proxy, make sure WebSocket upgrades are forwarded correctly. See the [Reverse Proxy guide](/docs/guides/reverse-proxy). +5. If the logs show `token is missing signing-key segment`, the token was created before v0.4.0. Rotate the token in the Hub UI and update `AUTH_TOKEN`. See [Upgrading](/docs/setup/upgrading). +6. If the Hub uses a self-signed certificate or a certificate from a private CA, mount the CA certificate into the Agent container and set `CA_CERT_FILE` to its path. +7. After changing the `agent.env` file, recreate the container with `docker compose up -d agent`. A plain restart does not reload the env file. + +## Agent logs "found the hub's secrets in the agent environment" + +The Agent received `APP_SECRET` or `OLD_APP_SECRET`, usually because it shares the `.env` file with the Hub (the default before v0.5.0). The Agent removes these variables at startup, so deployed applications can not read them, but you should give the Agent its own env file: + +1. Create an `agent.env` file next to your `docker-compose.yml` and move `HUB_URL`, `AUTH_TOKEN` and all other Agent settings from `.env` into it. +2. Change `env_file: .env` to `env_file: agent.env` in the `agent` service. +3. Recreate the Agent with `docker compose up -d agent`. ## Hub is not accessible / returns 502 @@ -31,8 +52,8 @@ description: Solutions to frequently encountered problems 1. Verify the Agent is online. 2. Check Agent logs for errors during the Docker Compose up/pull step. -3. Ensure the Agent has access to the Docker socket (`/var/run/docker.sock`). -4. If the Compose file references private images, confirm the Agent host has credentials to pull them (e.g., `docker login` was run on that host). +3. Ensure the Agent has access to the Docker API (`use_api_socket: true` in the default `docker-compose.yml`). +4. If the Compose file references private images, run `docker login` for the registry on the Agent host, as the user that starts the Agent, and recreate the Agent with `docker compose up -d agent`. With `use_api_socket`, Docker passes the registry credentials of that user to the Agent. The error `no credentials for found in the agent's Docker config` means that the Agent has no credentials for this registry. ## Repository sync fails with authentication error @@ -42,7 +63,7 @@ description: Solutions to frequently encountered problems 1. Confirm the auth token is still valid and has not expired or been revoked. 2. For token auth, verify the token has `read_repository` (GitLab) or `Contents: read` (GitHub) permissions. -3. Use the **Test Connection** button in the repository settings to check credentials without triggering a full sync. +3. Update the token in the repository settings under **Authentication**. OrcaCD tests the connection before saving and shows an error if the credentials are invalid. ## Webhook sync not triggering @@ -50,7 +71,7 @@ description: Solutions to frequently encountered problems **Checks:** -1. Confirm the webhook URL and secret in your Git provider match what OrcaCD shows in the repository settings. +1. Confirm the webhook URL and secret in your Git provider match what OrcaCD shows in the repository settings. OrcaCD responds with `401` for unknown repositories, wrong secrets and repositories whose sync type is not **Webhook**. See [Webhooks](/docs/configuration/webhooks#responses-and-rate-limits). 2. Check that the Hub is publicly reachable on the configured `APP_URL`. 3. Review the webhook delivery logs in your Git provider (GitHub: Settings → Webhooks → Recent Deliveries) to see if the request reached the Hub. 4. Verify the webhook is configured to send **push** events. diff --git a/public/assets/docs/account-setup.png b/public/assets/docs/account-setup.png index c759613..6b4bf39 100644 Binary files a/public/assets/docs/account-setup.png and b/public/assets/docs/account-setup.png differ diff --git a/public/assets/docs/agents-page.png b/public/assets/docs/agents-page.png index b000edc..354807a 100644 Binary files a/public/assets/docs/agents-page.png and b/public/assets/docs/agents-page.png differ diff --git a/public/assets/docs/application-details.png b/public/assets/docs/application-details.png new file mode 100644 index 0000000..277ee69 Binary files /dev/null and b/public/assets/docs/application-details.png differ diff --git a/public/assets/docs/application-general-settings.png b/public/assets/docs/application-general-settings.png new file mode 100644 index 0000000..01288df Binary files /dev/null and b/public/assets/docs/application-general-settings.png differ diff --git a/public/assets/docs/application-history-timeline.png b/public/assets/docs/application-history-timeline.png new file mode 100644 index 0000000..002a145 Binary files /dev/null and b/public/assets/docs/application-history-timeline.png differ diff --git a/public/assets/docs/application-image-updates.png b/public/assets/docs/application-image-updates.png new file mode 100644 index 0000000..567888f Binary files /dev/null and b/public/assets/docs/application-image-updates.png differ diff --git a/public/assets/docs/application-manifest-diff.png b/public/assets/docs/application-manifest-diff.png new file mode 100644 index 0000000..3877382 Binary files /dev/null and b/public/assets/docs/application-manifest-diff.png differ diff --git a/public/assets/docs/applications-page.png b/public/assets/docs/applications-page.png index 75ac767..f472c61 100644 Binary files a/public/assets/docs/applications-page.png and b/public/assets/docs/applications-page.png differ diff --git a/public/assets/docs/create-agent.png b/public/assets/docs/create-agent.png index 6d527b9..24b6b70 100644 Binary files a/public/assets/docs/create-agent.png and b/public/assets/docs/create-agent.png differ diff --git a/public/assets/docs/create-notification.png b/public/assets/docs/create-notification.png new file mode 100644 index 0000000..b8c8934 Binary files /dev/null and b/public/assets/docs/create-notification.png differ diff --git a/public/assets/docs/create-repository.png b/public/assets/docs/create-repository.png new file mode 100644 index 0000000..293a766 Binary files /dev/null and b/public/assets/docs/create-repository.png differ diff --git a/public/assets/docs/deploy-after-edit-toast.png b/public/assets/docs/deploy-after-edit-toast.png new file mode 100644 index 0000000..2e417d0 Binary files /dev/null and b/public/assets/docs/deploy-after-edit-toast.png differ diff --git a/public/assets/docs/orca-apps-screenshot.avif b/public/assets/docs/orca-apps-screenshot.avif index ff2fe7c..15ef14c 100644 Binary files a/public/assets/docs/orca-apps-screenshot.avif and b/public/assets/docs/orca-apps-screenshot.avif differ diff --git a/public/assets/docs/orca-apps-screenshot.png b/public/assets/docs/orca-apps-screenshot.png index 84ad7b0..15c9802 100644 Binary files a/public/assets/docs/orca-apps-screenshot.png and b/public/assets/docs/orca-apps-screenshot.png differ diff --git a/public/assets/docs/orca-apps-screenshot.webp b/public/assets/docs/orca-apps-screenshot.webp index 7ef71b7..fdb6c93 100644 Binary files a/public/assets/docs/orca-apps-screenshot.webp and b/public/assets/docs/orca-apps-screenshot.webp differ diff --git a/public/assets/docs/orca-sso-registration.png b/public/assets/docs/orca-sso-registration.png index 9087c39..184068a 100644 Binary files a/public/assets/docs/orca-sso-registration.png and b/public/assets/docs/orca-sso-registration.png differ diff --git a/public/assets/docs/overview-dashboard.png b/public/assets/docs/overview-dashboard.png new file mode 100644 index 0000000..c3821c0 Binary files /dev/null and b/public/assets/docs/overview-dashboard.png differ diff --git a/public/assets/docs/repositories-page.png b/public/assets/docs/repositories-page.png index 0c5239d..0e94a70 100644 Binary files a/public/assets/docs/repositories-page.png and b/public/assets/docs/repositories-page.png differ diff --git a/public/assets/docs/repository-signing.png b/public/assets/docs/repository-signing.png new file mode 100644 index 0000000..e57d944 Binary files /dev/null and b/public/assets/docs/repository-signing.png differ diff --git a/public/assets/docs/repository-sync-settings.png b/public/assets/docs/repository-sync-settings.png new file mode 100644 index 0000000..abcb48b Binary files /dev/null and b/public/assets/docs/repository-sync-settings.png differ diff --git a/public/assets/docs/security-sessions.png b/public/assets/docs/security-sessions.png new file mode 100644 index 0000000..4e9828f Binary files /dev/null and b/public/assets/docs/security-sessions.png differ diff --git a/public/assets/docs/sso-login-screen.png b/public/assets/docs/sso-login-screen.png index 4aba7c4..9211333 100644 Binary files a/public/assets/docs/sso-login-screen.png and b/public/assets/docs/sso-login-screen.png differ diff --git a/src/components/account-recovery-terminal.tsx b/src/components/account-recovery-terminal.tsx index 698bc0e..1a7de6d 100644 --- a/src/components/account-recovery-terminal.tsx +++ b/src/components/account-recovery-terminal.tsx @@ -4,7 +4,7 @@ export function AccountRecoveryTerminal() { return ( - $ docker compose exec hub /app/hub reset-password 019dc111-5220-77bc-9729-2335f88fa658 + $ docker compose exec hub /app/hub reset-password --email test@orcacd.dev {`