Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "oxc.oxc-vscode"
"editor.defaultFormatter": "oxc.oxc-vscode",
"[typescriptreact]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
}
}
14 changes: 14 additions & 0 deletions cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "node_modules/@fumadocs/cli/dist/schema.json",
Comment thread
timokoessler marked this conversation as resolved.
Outdated
"aliases": {
"uiDir": "./components/ui",
"componentsDir": "./components",
"layoutDir": "./layouts",
"cssDir": "./styles",
"libDir": "./lib"
},
"baseDir": "src",
"uiLibrary": "radix-ui",
"framework": "tanstack-start",
"commands": {}
}
18 changes: 16 additions & 2 deletions content/docs/advanced/security.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
---
title: Security
description: Secure your OrcaCD deployment with hardening
description: Secure your OrcaCD deployment
---

TODO:
OrcaCD is designed to be safe by default, but of course, there are always additional steps you can take to further secure your deployment. Here are some best practices to consider:

## Harden your Deployment

- Disable password authentication for the Hub and use a secure OIDC provider instead, that enforces strong authentication methods, including secure multi-factor authentication (MFA).
- Always run the hub behind a secure reverse proxy and ensure that all communicationis encrypted using TLS.
- Make sure to configure the `TRUSTED_PROXIES` environment variable correctly to prevent IP spoofing attacks.

## 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).
1 change: 0 additions & 1 deletion content/docs/configuration/env-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ 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.

## General
Expand Down
65 changes: 59 additions & 6 deletions content/docs/setup/installation.mdx
Original file line number Diff line number Diff line change
@@ -1,19 +1,72 @@
---
title: Installation
description: Get OrcaCD running quickly with Docker installation
description: Get OrcaCD running quickly with Docker
---

## Installation with Docker
import { Step, Steps } from "fumadocs-ui/components/steps";
Comment thread
timokoessler marked this conversation as resolved.
Outdated
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
Comment thread
timokoessler marked this conversation as resolved.
Outdated

1. 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:
<Steps>
<Step>

```bash
## Start Hub and Agent

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:

```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
```

2. Edit the `.env` file so that it fits your needs. See the environment variables section for more information.
```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
```

Edit the `.env` file according to the instructions in the file. You can also customize other environment variables as needed.
See the [environment variables page](../configuration/env-variables) for more details.

<Callout>
Remove the Agent part from the compose file if you don't want to deploy it to the same machine as
the Hub.
</Callout>

Start the Hub and the Agent:

```bash
docker compose up -d
```

</Step>
<Step>

3. Run `docker compose up -d`
## Configure your Reverse Proxy

See the [reverse proxy guide](../guides/reverse-proxy) for instructions on how to configure a reverse proxy for your Hub.

</Step>
<Step>

## Create an Admin Account

Create an admin account on `https://<your-app-url>/login`

Todo: Add image

</Step>
<Step>

## Connect your first Agent

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.

Todo: Add image

</Step>
<Step>
## Start Deploying

Add your first repository and create your first deployment.

</Step>
</Steps>
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"dependencies": {
"@orama/orama": "^3.1.18",
"@radix-ui/react-tabs": "^1.1.13",
"@tanstack/react-router": "1.168.23",
"@tanstack/react-router-devtools": "1.166.13",
"@tanstack/react-start": "1.167.42",
Expand Down
9 changes: 9 additions & 0 deletions src/components/steps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ReactNode } from "react";

export function Steps({ children }: { children: ReactNode }) {
return <div className="fd-steps">{children}</div>;
}

export function Step({ children }: { children: ReactNode }) {
return <div className="fd-step">{children}</div>;
}
199 changes: 199 additions & 0 deletions src/components/tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"use client";

import * as React from "react";
import {
type ComponentProps,
createContext,
type ReactNode,
useContext,
useEffect,
useId,
useMemo,
useState,
} from "react";
import { cn } from "../lib/cn";
import * as Unstyled from "./ui/tabs";

type CollectionKey = string | symbol;

export interface TabsProps extends Omit<
ComponentProps<typeof Unstyled.Tabs>,
"value" | "onValueChange"
> {
/**
* Use simple mode instead of advanced usage as documented in https://radix-ui.com/primitives/docs/components/tabs.
*/
items?: string[];

/**
* Shortcut for `defaultValue` when `items` is provided.
*
* @defaultValue 0
*/
defaultIndex?: number;

/**
* Additional label in tabs list when `items` is provided.
*/
label?: ReactNode;
}

const TabsContext = createContext<{
items?: string[];
collection: CollectionKey[];
} | null>(null);

function useTabContext() {
const ctx = useContext(TabsContext);
if (!ctx) {
throw new Error("You must wrap your component in <Tabs>");
}
return ctx;
}

export function TabsList(props: React.ComponentPropsWithRef<typeof Unstyled.TabsList>) {
return (
<Unstyled.TabsList
{...props}
className={cn(
"flex gap-3.5 text-fd-secondary-foreground overflow-x-auto px-4 not-prose",
props.className,
)}
/>
);
}

export function TabsTrigger(props: React.ComponentPropsWithRef<typeof Unstyled.TabsTrigger>) {
return (
<Unstyled.TabsTrigger
{...props}
className={cn(
"inline-flex items-center gap-2 whitespace-nowrap text-fd-muted-foreground border-b border-transparent py-2 text-sm font-medium transition-colors [&_svg]:size-4 hover:text-fd-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-fd-primary data-[state=active]:text-fd-primary",
props.className,
)}
/>
);
}

export function Tabs({
ref,
className,
items,
label,
defaultIndex = 0,
defaultValue = items ? escapeValue(items[defaultIndex]) : undefined,
...props
}: TabsProps) {
const [value, setValue] = useState(defaultValue);
const collection = useMemo<CollectionKey[]>(() => [], []);

return (
<Unstyled.Tabs
ref={ref}
className={cn(
"flex flex-col overflow-hidden rounded-xl border bg-fd-secondary my-4",
className,
)}
value={value}
onValueChange={(v: string) => {
if (items && !items.some((item) => escapeValue(item) === v)) {
return;
}
setValue(v);
}}
{...props}
>
{items && (
<TabsList>
{label && <span className="text-sm font-medium my-auto me-auto">{label}</span>}
{items.map((item) => (
<TabsTrigger key={item} value={escapeValue(item)}>
{item}
</TabsTrigger>
))}
</TabsList>
)}
<TabsContext.Provider value={useMemo(() => ({ items, collection }), [collection, items])}>
{props.children}
</TabsContext.Provider>
</Unstyled.Tabs>
);
}

export interface TabProps extends Omit<ComponentProps<typeof Unstyled.TabsContent>, "value"> {
/**
* Value of tab, detect from index if unspecified.
*/
value?: string;
}

export function Tab({ value, ...props }: TabProps) {
const { items } = useTabContext();
const resolved =
value ??
// eslint-disable-next-line react-hooks/rules-of-hooks -- `value` is not supposed to change
items?.at(useCollectionIndex());
if (!resolved) {
throw new Error(
"Failed to resolve tab `value`, please pass a `value` prop to the Tab component.",
);
}

return (
<TabsContent value={escapeValue(resolved)} {...props}>
{props.children}
</TabsContent>
);
}

export function TabsContent({
value,
className,
...props
}: ComponentProps<typeof Unstyled.TabsContent>) {
return (
<Unstyled.TabsContent
value={value}
forceMount
className={cn(
"p-4 text-[0.9375rem] bg-fd-background rounded-xl outline-none prose-no-margin data-[state=inactive]:hidden [&>figure:only-child]:-m-4 [&>figure:only-child]:border-none",
className,
)}
{...props}
>
{props.children}
</Unstyled.TabsContent>
);
}

/**
* Inspired by Headless UI.
*
* Return the index of children, this is made possible by registering the order of render from children using React context.
* This is supposed by work with pre-rendering & pure client-side rendering.
*/
function useCollectionIndex() {
const key = useId();
const { collection } = useTabContext();

useEffect(() => {
return () => {
const idx = collection.indexOf(key);
if (idx !== -1) {
collection.splice(idx, 1);
}
};
}, [key, collection]);

if (!collection.includes(key)) {
collection.push(key);
}
return collection.indexOf(key);
}

/**
* only escape whitespaces in values in simple mode
*/
function escapeValue(v: string): string {
return v.toLowerCase().replace(/\s/, "-");
}
Loading