> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corsair.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start

> A working integration in five steps, powered by Hub.

export const WindowsMigrationNote = () => <div className="not-prose" style={calloutStyle}>
    <WindowsIcon />
    <div>
      <div style={titleStyle}>PowerShell syntax</div>
      <div style={bodyStyle}>
        Bash redirection and <span style={codeStyle}>$VAR</span> env vars do not work in PowerShell.
        Use the Windows commands shown in this tab instead.
      </div>
    </div>
  </div>;

export const GenerateKEK = () => {
  const [key, setKey] = useState('');
  const [copiedCmd, setCopiedCmd] = useState(false);
  const [copiedEnv, setCopiedEnv] = useState(false);
  const generate = () => {
    const bytes = new Uint8Array(32);
    crypto.getRandomValues(bytes);
    setKey(btoa(String.fromCharCode(...bytes)));
  };
  useEffect(() => {
    generate();
  }, []);
  const copy = (text, setter) => {
    navigator.clipboard.writeText(text);
    setter(true);
    setTimeout(() => setter(false), 2000);
  };
  const envValue = `CORSAIR_KEK="${key || 'loading...'}"`;
  const blockStyle = {
    borderRadius: '0.5rem',
    overflow: 'hidden',
    border: '1px solid var(--border, rgba(255,255,255,0.1))',
    backgroundColor: 'var(--code-background, #0d1117)',
    fontFamily: 'var(--font-mono, ui-monospace, monospace)',
    fontSize: '0.8125rem',
    lineHeight: '1.6'
  };
  const headerStyle = {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: '0.375rem 0.75rem',
    borderBottom: '1px solid var(--border, rgba(255,255,255,0.1))'
  };
  const labelStyle = {
    fontSize: '0.75rem',
    color: 'var(--muted-foreground, rgba(255,255,255,0.4))'
  };
  const btnGroupStyle = {
    display: 'flex',
    alignItems: 'center',
    gap: '0.375rem'
  };
  const btnStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    gap: '0.3rem',
    padding: '0.2rem 0.6rem',
    borderRadius: '0.375rem',
    fontSize: '0.7rem',
    fontWeight: 500,
    cursor: 'pointer',
    transition: 'opacity 0.15s',
    border: '1px solid var(--border, rgba(255,255,255,0.15))',
    backgroundColor: 'var(--muted, rgba(255,255,255,0.06))',
    color: 'var(--foreground, rgba(255,255,255,0.75))',
    fontFamily: 'var(--font-sans, sans-serif)'
  };
  const generateBtnStyle = {
    ...btnStyle,
    backgroundColor: 'var(--primary, #2d7387)',
    border: '1px solid transparent',
    color: '#fff'
  };
  const codeStyle = {
    display: 'block',
    padding: '0.75rem 1rem',
    color: 'var(--code-foreground, rgba(255,255,255,0.85))',
    whiteSpace: 'pre',
    overflowX: 'auto',
    margin: 0,
    background: 'none'
  };
  return <div className="not-prose" style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '0.625rem'
  }}>
      <div style={blockStyle}>
        <div style={headerStyle}>
          <span style={labelStyle}>bash</span>
          <button style={btnStyle} onClick={() => copy('openssl rand -base64 32', setCopiedCmd)}>
            {copiedCmd ? '✓ Copied' : 'Copy'}
          </button>
        </div>
        <pre style={{
    margin: 0
  }}><code style={codeStyle}>openssl rand -base64 32</code></pre>
      </div>
      <div style={blockStyle}>
        <div style={headerStyle}>
          <span style={labelStyle}>.env</span>
          <div style={btnGroupStyle}>
            <button style={generateBtnStyle} onClick={generate}>
              Regenerate
            </button>
            <button style={btnStyle} onClick={() => copy(envValue, setCopiedEnv)}>
              {copiedEnv ? '✓ Copied' : 'Copy'}
            </button>
          </div>
        </div>
        <pre style={{
    margin: 0
  }}>
          <code style={codeStyle}>{envValue}</code>
        </pre>
      </div>
    </div>;
};

The quickest path to a working integration is **Hub**. It hosts the OAuth connect, approval, and webhook surfaces, so there are no connect pages, callback routes, or per-environment redirect URIs to build. Credentials are still encrypted and stored in your own database — Hub stores none.

<Note>
  Want to host those surfaces yourself instead? Every step below is the same; swap the `hub` block for `manual`. See [Manual or Hub](/hub/manual-vs-hub).
</Note>

<Steps>
  <Step>
    ## Install

    <CodeGroup>
      ```bash npm theme={null}
      npm install corsair @corsair-dev/github
      ```

      ```bash yarn theme={null}
      yarn add corsair @corsair-dev/github
      ```

      ```bash pnpm theme={null}
      pnpm install corsair @corsair-dev/github
      ```

      ```bash bun theme={null}
      bun add corsair @corsair-dev/github
      ```
    </CodeGroup>

    <Tip>
      `@corsair-dev/github` is one plugin. Every service is its own `@corsair-dev/*` package — find the one you need, and its exact install id, in the [**Plugins**](/guides/plugins) catalog.
    </Tip>
  </Step>

  <Step>
    ## Set your environment

    Create a project in the [Hub dashboard](https://hub.corsair.dev/dashboard) and copy the **development** API key and signing secret. Then generate a KEK — Corsair encrypts every stored credential with it:

    <GenerateKEK />

    ```bash .env theme={null}
    CORSAIR_KEK=your-generated-kek
    CORSAIR_DEV_API_KEY=ck_dev_...
    CORSAIR_DEV_SIGNING_SECRET=...
    APP_URL=http://localhost:3000
    ```

    <Warning>
      Keep your KEK safe. Lose it and you lose access to every stored credential. Treat it like a root password.
    </Warning>
  </Step>

  <Step>
    ## Create the database

    Corsair stores data in four tables. SQLite is the fastest way to start:

    <CodeGroup>
      ```bash npm theme={null}
      npm install better-sqlite3
      ```

      ```bash yarn theme={null}
      yarn add better-sqlite3
      ```

      ```bash pnpm theme={null}
      pnpm install better-sqlite3
      ```

      ```bash bun theme={null}
      bun add better-sqlite3
      ```
    </CodeGroup>

    <Accordion title="View migration SQL">
      ```sql migration.sql theme={null}
      CREATE TABLE IF NOT EXISTS corsair_integrations (
          id TEXT PRIMARY KEY,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL,
          name TEXT NOT NULL,
          config TEXT NOT NULL DEFAULT '{}',
          dek TEXT NULL
      );

      CREATE TABLE IF NOT EXISTS corsair_accounts (
          id TEXT PRIMARY KEY,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL,
          tenant_id TEXT NOT NULL,
          integration_id TEXT NOT NULL,
          config TEXT NOT NULL DEFAULT '{}',
          dek TEXT NULL,
          FOREIGN KEY (integration_id) REFERENCES corsair_integrations(id)
      );

      CREATE TABLE IF NOT EXISTS corsair_entities (
          id TEXT PRIMARY KEY,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL,
          account_id TEXT NOT NULL,
          entity_id TEXT NOT NULL,
          entity_type TEXT NOT NULL,
          version TEXT NOT NULL,
          data TEXT NOT NULL DEFAULT '{}',
          FOREIGN KEY (account_id) REFERENCES corsair_accounts(id)
      );

      CREATE TABLE IF NOT EXISTS corsair_events (
          id TEXT PRIMARY KEY,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL,
          account_id TEXT NOT NULL,
          event_type TEXT NOT NULL,
          payload TEXT NOT NULL DEFAULT '{}',
          status TEXT,
          FOREIGN KEY (account_id) REFERENCES corsair_accounts(id)
      );
      ```
    </Accordion>

    <Tabs>
      <Tab title="macOS / Linux">
        ```bash theme={null}
        sqlite3 corsair.db < migration.sql
        ```
      </Tab>

      <Tab title="Windows (PowerShell)">
        <WindowsMigrationNote />

        ```powershell theme={null}
        Get-Content migration.sql | sqlite3 corsair.db
        ```
      </Tab>
    </Tabs>

    Using Postgres, Drizzle, or Prisma instead? See [Database](/concepts/database) for each option.
  </Step>

  <Step>
    ## Configure Corsair

    Wire your database, KEK, and Hub keys together in `src/server/corsair.ts`:

    ```ts src/server/corsair.ts theme={null}
    import 'dotenv/config';
    import Database from 'better-sqlite3';
    import { createCorsair } from 'corsair';
    import { github } from '@corsair-dev/github';

    const db = new Database('corsair.db');

    export const corsair = createCorsair({
        plugins: [github({ authType: 'managed' })],
        database: db,
        kek: process.env.CORSAIR_KEK!,
        hub: {
            projectApiKey: process.env.CORSAIR_DEV_API_KEY!,
            signingSecret: process.env.CORSAIR_DEV_SIGNING_SECRET!,
        },
    });
    ```

    Mount the handler once — it serves Hub delivery and the management API. In development, Hub auto-detects your localhost delivery URL:

    ```ts app/api/corsair/[[...path]]/route.ts theme={null}
    import { toNextJsHandler } from 'corsair';
    import { corsair } from '@/server/corsair';

    export const { GET, POST, OPTIONS } = toNextJsHandler(corsair, {
        basePath: '/api/corsair',
    });
    ```

    Add more plugins later — `slack()`, `linear()`, `gmail()` — by appending to the array.
  </Step>

  <Step>
    ## Connect and call

    Mint a connect link and send the user to it. Hub hosts the connect page and delivers the result back to your app — no connect page or OAuth callback to build:

    ```ts connect.ts theme={null}
    const { connectUrl } = await corsair.manage.connect.createLink({
        plugin: 'github',
        tenantId: 'acme',
    });
    // redirect the user's browser to connectUrl
    ```

    Once connected, call any endpoint. Responses are also cached in your database for instant reads:

    ```ts usage.ts theme={null}
    const repos = await corsair.github.api.repositories.list({});
    ```

    Want an agent to call endpoints on its own? See [MCP Adapters](/mcp-adapters/mcp-adapters).
  </Step>
</Steps>

***

## What's next

<CardGroup cols={2}>
  <Card title="Use with AI" href="/mcp-adapters/mcp-adapters">
    Give an agent the four Corsair tools and let it discover and call any endpoint.
  </Card>

  <Card title="Hub overview" href="/hub/overview">
    How the relay works, and why Hub stores none of your credentials.
  </Card>

  <Card title="Environments" href="/hub/environments">
    Development vs production keys — switch to production when you deploy.
  </Card>

  <Card title="Multi-Tenancy" href="/concepts/multi-tenancy">
    Building a product? Flip one flag and every user gets their own data and credentials.
  </Card>

  <Card title="Database options" href="/concepts/database">
    Postgres, Drizzle, Prisma, and the four tables Corsair uses.
  </Card>

  <Card title="Provisioning" href="/concepts/provisioning">
    Seed credentials and provision tenants from the `corsair` CLI or `setupCorsair`.
  </Card>
</CardGroup>
