From zero to live backend in 5 minutes

Quickstart Guide

Deploy your backend on Aethelos Base in eight steps. Get an isolated PostgreSQL database, a REST API and typed SDK — with hashed, rotatable keys and full white-labeling so it looks like you built it.

01

Create your account

Sign up in seconds — email and password, no credit card needed. Add a second factor and verify your email from Account Settings once you're in.

Sign up free
02

Create a project

Each project gets its own isolated PostgreSQL database, provisioned instantly.

Go to dashboard
03

Generate an API key (shown once)

Keys are hashed with SHA-256 at rest — we store only a fingerprint and the last 4 characters, so a leak of our database can never expose your keys. Copy the secret the one time it's revealed; you can't see it again.

# Shown once, then never again:
aeth_sk_9f8e7d6c5b4a3f21...

# In every later screen the key is masked:
aeth_sk_9f8e••••••••••••••c7d2

# Pick a role and (optionally) an expiry:
#   read       — read-only queries
#   readwrite  — read + write (default)
#   admin      — everything, including DDL
04

Connect your app

Use the REST API directly, or generate a typed client SDK for your project.

// ── Option A: REST API ──────────────────────────
const BASE = "https://api.yourdomain.com/v1/db";   // or your custom domain
const KEY  = process.env.BASE_API_KEY;             // aeth_sk_... (from step 03)

// List all users
const res = await fetch(`${BASE}/users`, {
  headers: { Authorization: `Bearer ${KEY}` }
});
const { data } = await res.json();

// Insert a new product
await fetch(`${BASE}/products`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Kente Cloth",
    price: 250.00,
    inventory_count: 45
  })
});

// ── Option B: Auto-generated SDK ────────────────
// GET /api/v1/sdk returns a ready-to-import JS client.
// When white-label is on, the SDK is branded to YOUR domain and carries
// no Aethelos markers.

import { createClient } from "./your-client.js";
const db = createClient();

const users    = await db.users.list();
const product  = await db.products.get("p1");
const newRow   = await db.orders.create({
  user_id: "u1",
  total_amount: 335.00,
  status: "pending"
});

// ── End-user auth (Supabase-style JWT) ──────────
// auth.* returns a session; the SDK then auto-sends the JWT alongside the
// API key on every later db/storage call, so row-level security scopes the
// rows (and files) to that signed-in end user.
await db.auth.signUp("ada@example.com", "hunter2");
const { data: me } = await db.auth.me();

// ── Object storage (bytes live in the project DB) ─
await db.storage.upload("avatars", "ada.png", file, file.type);
const blob = await db.storage.download("avatars", "ada.png");
05

Create tables via SQL

Use the built-in query console to define your schema, or push DDL through the API.

-- Run this in the Query Console (Ctrl+K → "Query Console")

CREATE TABLE users (
  id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email      TEXT UNIQUE NOT NULL,
  full_name  TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE products (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name            TEXT NOT NULL,
  price           DECIMAL(10,2) NOT NULL,
  inventory_count INT DEFAULT 0,
  created_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE orders (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id       UUID REFERENCES users(id),
  total_amount  DECIMAL(10,2) NOT NULL,
  status        TEXT DEFAULT 'pending',
  ordered_at    TIMESTAMPTZ DEFAULT now()
);
06

Rotate, expire, revoke

Treat keys as disposable credentials. One-click rotation issues a fresh secret and immediately invalidates the old one. Set expiries on long-lived keys, change a key's role without regenerating it, and revoke instantly if something leaks.

# Rotation keeps a lineage link (rotated_from) for auditing.
# After rotating, the previous key is rejected with a neutral 401.

# Every key action is written to the immutable audit trail
# (Account Settings → Activity):
#   api_key_created · api_key_rotated · api_key_revoked · api_key_role_changed
07

Make it yours (white-label)

Point a custom domain at your project, verify it with a DNS TXT record, and Base stops looking like Base. Mask platform headers, use your own key prefix, and serve the SDK from your domain — your users never see who runs the backend.

# 1. Add api.yourdomain.com in Project Settings → White-label.
# 2. Create the DNS TXT record we show you:
#      _aethelos-verify.yourdomain.com  =  dom_v1a2b3c4...
# 3. Click Verify. Once verified, TLS is issued automatically.

# Then, per project:
#   • Custom key prefix   → yld_sk_...  instead of  aeth_sk_...
#   • Mask platform headers → no X-Aethelos-* response headers
#   • Neutral errors       → "Service temporarily unavailable", not stack traces
#   • SDK served from       https://api.yourdomain.com/v1/sdk
08

Ship it

Your backend is live. Monitor telemetry, manage API keys, and scale.

# Test your live API with curl

curl -X GET https://api.yourdomain.com/v1/db/users \
  -H "Authorization: Bearer $BASE_API_KEY"

# Response:
{
  "data": [
    { "id": "u1", "email": "kwame@example.com", "full_name": "Kwame Asante" },
    { "id": "u2", "email": "ama@example.com",   "full_name": "Ama Mensah" }
  ],
  "count": 2
}

Tune each project

Project Settings exposes the levers you need to run a public API safely. Changes take effect immediately and are recorded in the audit trail.

CORS origins

Restrict which browser origins may call your API — pin them to your real domains instead of *.

Rate limiting

Cap requests per window, enforced centrally in the database so it holds across every server process.

IP allowlist

Optionally lock the API to known server IPs for machine-to-machine backends.

Maintenance mode

Return a neutral 503 for the whole project while you upgrade, without touching the key.

How your data is protected

  • API keys are hashed (SHA-256) at rest and revealed only once — the plaintext is never stored or logged.
  • Passwords use scrypt with a per-user salt; older hashes are upgraded automatically on next sign-in.
  • Two-factor authentication (TOTP) seeds are encrypted at rest with AES-256-GCM.
  • Login attempts are throttled and lock out temporarily after repeated failures.
  • A tamper-evident activity log records every auth, key and config change with IP and user-agent.
  • Platform responses send hardened security headers and can omit Aethelos branding entirely.
  • Each project runs in its own PostgreSQL database, isolated from every other tenant.
  • Row-level security: when an end-user JWT is presented, data and storage calls run as a non-privileged role scoped to that user's rows.
  • Usage is metered per plan — disk, bandwidth (egress) and monthly active users warn at 80/90% and soft-block at 100% (402/413/429), never deleting data.

REST API Endpoints

All endpoints require Authorization: Bearer <your key>. With white-label on, serve them from your own domain (https://api.yourdomain.com/v1/db/...).

GET/api/v1/db/:table
POST/api/v1/db/:table
GET/api/v1/db/:table/:id
PUT/api/v1/db/:table/:id
DELETE/api/v1/db/:table/:id
GET/api/v1/sdk
POST/api/v1/auth/:projectId/signup
POST/api/v1/auth/:projectId/token
GET/api/v1/auth/me
PUT/api/v1/storage/:bucket/:path
GET/api/v1/storage/:bucket

Connect from any framework

Next.js / React

const res = await fetch(`${BASE}/users`, {
  headers: { Authorization: `Bearer ${KEY}` }
});
const { data } = await res.json();

Python

import requests
res = requests.get(
  f"{BASE}/users",
  headers={"Authorization": f"Bearer {KEY}"}
)
data = res.json()["data"]

cURL

curl "$BASE/users" \
  -H "Authorization: Bearer $KEY"

Flutter / Dart

final res = await http.get(
  Uri.parse("$BASE/users"),
  headers: {"Authorization": "Bearer $KEY"},
);
final data = jsonDecode(res.body);

Ready to build?

Create your free account and deploy your first backend in minutes.

Get started free