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.
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 freeEach project gets its own isolated PostgreSQL database, provisioned instantly.
Go to dashboardKeys 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
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");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() );
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
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
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
}Project Settings exposes the levers you need to run a public API safely. Changes take effect immediately and are recorded in the audit trail.
Restrict which browser origins may call your API — pin them to your real domains instead of *.
Cap requests per window, enforced centrally in the database so it holds across every server process.
Optionally lock the API to known server IPs for machine-to-machine backends.
Return a neutral 503 for the whole project while you upgrade, without touching the key.
All endpoints require Authorization: Bearer <your key>. With white-label on, serve them from your own domain (https://api.yourdomain.com/v1/db/...).
/api/v1/db/:table/api/v1/db/:table/api/v1/db/:table/:id/api/v1/db/:table/:id/api/v1/db/:table/:id/api/v1/sdk/api/v1/auth/:projectId/signup/api/v1/auth/:projectId/token/api/v1/auth/me/api/v1/storage/:bucket/:path/api/v1/storage/:bucketconst res = await fetch(`${BASE}/users`, {
headers: { Authorization: `Bearer ${KEY}` }
});
const { data } = await res.json();import requests
res = requests.get(
f"{BASE}/users",
headers={"Authorization": f"Bearer {KEY}"}
)
data = res.json()["data"]curl "$BASE/users" \ -H "Authorization: Bearer $KEY"
final res = await http.get(
Uri.parse("$BASE/users"),
headers: {"Authorization": "Bearer $KEY"},
);
final data = jsonDecode(res.body);