# Quickstart

Create a tenant, seal an OpenAI key and call the API through Seal from your app. About ten minutes, free.

Source: https://seal.zone/docs/quickstart

You need Linux or macOS, an OpenAI API key, and Python, Node, Go or Rust. The free Developer plan covers it.

## Create your tenant

### Sign up

Sign in at [console.seal.zone](https://console.seal.zone) and create a workspace.

### Name the tenant and choose an admin key

Click **New tenant**. Pick a tenant id like `acme`. It can't change later.

Your admin key signs every change. Seal never holds it.

- **Passkey.** The easiest choice.
- **Key file.** `admin.key`, for signing from scripts with `--key-file`.

### Choose who runs the enclave and how long risky changes wait

Pick **Seal runs it**. For risky changes, **None** is fine for a trial. Lengthen it before real keys go in ([why](https://seal.zone/docs/changes)).

Click **Review and sign** and sign with your admin key.

### Add a backup key

Lose your only admin key and nobody can change the tenant again. Add a second one under **Admins**.

## Set up the CLI

```shell
# with Python 3.9 or later
pip install sealzone
# or with Rust
cargo install sealzone-cli
```

```shell
seal login
seal use acme
```

`seal use` picks your tenant and verifies its signed settings.

## Grant an app

An app is a name your blobs list. Here it proves who it is with an app key file.

```shell
seal grant app checkout key --generate --out checkout.key
```

The CLI opens the console for you to sign, or add `--key-file admin.key`. In the cloud, use [your cloud's identity](https://seal.zone/docs/apps) instead.

## Seal your OpenAI key

```shell
seal secret --to https://api.openai.com/v1 --app checkout > openai.blob
```

Paste your key at the hidden prompt. The CLI checks the enclave's attestation, then encrypts the key on your machine. The blob works only for checkout and only toward that URL. Store it anywhere. [Sealing a secret](https://seal.zone/docs/sealing) lists the options.

## Call OpenAI from your app

Copy `SEAL_TENANT` from the console's **Connect** page or `seal status`.

```shell
export SEAL_ENDPOINT=https://acme.enclave.seal.zone:8443
export SEAL_TENANT=acme@<root id>
export SEAL_APP_KEY="$(cat checkout.key)"
export OPENAI_API_BLOB="$(cat openai.blob)"
```

Open a session. You get a local URL and a stand-in key that only this process can use.
```python
# pip install sealzone openai
import os, seal
from openai import OpenAI

with seal.session(os.environ["OPENAI_API_BLOB"]) as s:
    client = OpenAI(base_url=s.url, api_key=s.key)
    reply = client.responses.create(model="gpt-5", input="Say hello")
    print(reply.output_text)
```

```typescript
// npm install @sealzone/sdk openai
import { Client } from "@sealzone/sdk";
import OpenAI from "openai";

const seal = await Client.fromEnv();
const s = await seal.session(process.env.OPENAI_API_BLOB!, { kind: "http" });
try {
  const openai = new OpenAI({ baseURL: s.url, apiKey: s.key });
  const reply = await openai.responses.create({ model: "gpt-5", input: "Say hello" });
  console.log(reply.output_text);
} finally {
  await s.close();
}
```

```go
// go get seal.zone/go  (needs cgo)
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"

	seal "seal.zone/go"
)

func main() {
	ctx := context.Background()
	client, err := seal.FromEnv(ctx)
	if err != nil {
		panic(err)
	}
	s, err := client.Session(ctx, os.Getenv("OPENAI_API_BLOB"), seal.WithKind(seal.KindHTTP))
	if err != nil {
		panic(err)
	}
	defer s.Close(ctx)

	body := strings.NewReader(`{"model": "gpt-5", "input": "Say hello"}`)
	req, _ := http.NewRequestWithContext(ctx, "POST", s.URL()+"/responses", body)
	req.Header.Set("Authorization", "Bearer "+s.Key())
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
```

```rust
// cargo add sealzone serde_json
// cargo add tokio --features full
// cargo add reqwest --features json
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let seal = seal::Client::from_env().await?;
    let blob = std::env::var("OPENAI_API_BLOB")?;
    let s = seal.session(&blob).http().await?;

    let reply = reqwest::Client::new()
        .post(format!("{}/responses", s.url()))
        .bearer_auth(s.key())
        .json(&serde_json::json!({ "model": "gpt-5", "input": "Say hello" }))
        .send()
        .await?
        .text()
        .await?;
    println!("{reply}");

    s.close().await?;
    Ok(())
}
```
The first call can take about a minute while your idle replica starts. Your code never touched the real key. The enclave added it on the way out and masked any copy in the response. **Telemetry** in the console shows each step.

**About the warning.**

The app key and the blob both sit in environment variables. Anyone who reads them can use the key as your app. Fine on a laptop. In production, use a [cloud identity](https://seal.zone/docs/apps).

## Next

- [Key ideas](https://seal.zone/docs/concepts): blobs, sessions, tenants and admins.
- [Apps and identities](https://seal.zone/docs/apps): cloud identities and network rules.
- [Using a blob](https://seal.zone/docs/sessions): databases, long-running processes and errors.
- [What an attacker gets](https://seal.zone/docs/threat-model): what root on your server can and can't do.
