FullStacked Cloud · Documentation docs.fullstacked.cloud
View ?format=md View ?format=json
AI agents and scripts: https://docs.fullstacked.cloud/?format=md https://docs.fullstacked.cloud/?format=json MCP:docs_get({ topic: "runtime" })

Current User (/api/v1/me)

A project finds out who is running it by calling GET ${process.env.AUTH_URL}/api/v1/me with Authorization: Bearer ${process.env.AUTH_TOKEN}. The runtime's fetch sends it from the Go core, so there is no CORS issue and no backend to write: call it directly from index.tsx, a helper module or an action. For permissions, groups and quotas, call GET ${process.env.AUTH_URL}/api/v1/user/access.

Common mistakes

MistakeWhat happensDo instead
fetch("/api/v1/me")In an app, looks up the local files out/api/v1/me then <project>/api/v1/me, not found; in an action it throws (invalid URL)fetch(`${process.env.AUTH_URL}/api/v1/me`, ...)
A local http.createServer proxyhttp has no createServer in the runtimeCall fetch from the code that needs the user
Hardcoding https://auth.fullstacked.cloudBreaks in the other environmentprocess.env.AUTH_URL
No Authorization header401Authorization: `Bearer ${process.env.AUTH_TOKEN}`

GET /api/v1/me

  • URL: ${process.env.AUTH_URL}/api/v1/me (alias /v1/me), https://auth.fullstacked.cloud/api/v1/me here.
  • Auth: Authorization: Bearer <fsc_* token> (any native token: session, personal access token, agent, enterprise session).
  • Returns identity only, never grants. Errors: 401 (missing, unknown, revoked or expired token), 403 (account or authority suspended), body { "error", "message", "statusCode", "details" }.
json
{
  "status": "success",
  "data": {
    "id": "usr_4f1c9a2b7d3e8f6a5b0c1d2e",
    "username": "alice",
    "slug": "alice",
    "name": "Alice Martin",
    "email": "[email protected]",
    "email_verified": true,
    "avatar_url": null,
    "type": "user",
    "status": "active",
    "plan": "base",
    "plan_name": "Base",
    "has_run_client_gui": true,
    "restricted": false,
    "created_at": "2026-09-20T14:32:00.000Z"
  }
}
FieldMeaning
idusr_<24 hex> for platform users; <authorityId>:<externalUserId> for enterprise (BYOA) users. Use it as the stable user key.
username, slugSame value: the user's handle (Git namespace). Enterprise users: the external user id.
name, emailMay be null. email is always a verified address for platform users.
email_verifiedPlatform users only.
avatar_urlAlways null: render initials or your own avatar.
typeuser, operator, agent (token issued to an AI agent) or service_account (Edge token).
plan, plan_namePlatform users only: base or pro and its display name.
authority_idEnterprise users only: the issuing authority (ath_<slug>).
has_run_client_guiWhether this principal has run the FullStacked Cloud Client GUI.
restrictedtrue while a platform user has not been invited to any asset yet.
created_atAccount creation (enterprise users: token issue time), ISO 8601.

Helper

ts
// user.ts
export interface CurrentUser {
    id: string;
    username: string;
    slug: string;
    name: string | null;
    email: string | null;
    email_verified?: boolean;
    avatar_url: string | null;
    type: "user" | "operator" | "agent" | "service_account";
    status: string;
    plan?: "base" | "pro";
    plan_name?: string;
    authority_id?: string;
    has_run_client_gui: boolean;
    restricted: boolean;
    created_at: string;
}

export async function getCurrentUser(): Promise<CurrentUser> {
    const { AUTH_URL, AUTH_TOKEN } = process.env;
    if (!AUTH_URL || !AUTH_TOKEN) {
        throw new Error("AUTH_URL / AUTH_TOKEN missing: run from the FullStacked Cloud Client GUI or with -e AUTH_TOKEN=...");
    }
    const res = await fetch(`${AUTH_URL}/api/v1/me`, {
        headers: { Authorization: `Bearer ${AUTH_TOKEN}`, Accept: "application/json" },
    });
    if (!res.ok) throw new Error(`GET /api/v1/me failed (${res.status}): ${await res.text()}`);
    const body = (await res.json()) as { status: string; data: CurrentUser };
    return body.data;
}
tsx
// index.tsx
import React, { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { getCurrentUser, type CurrentUser } from "./user";

function App() {
    const [user, setUser] = useState<CurrentUser | null>(null);
    const [error, setError] = useState<string | null>(null);
    useEffect(() => {
        getCurrentUser().then(setUser, (e) => setError(String(e?.message ?? e)));
    }, []);
    if (error) return <p>Not signed in: {error}</p>;
    if (!user) return <p>Loading...</p>;
    return <p>{user.name ?? user.username} ({user.email ?? "no email"})</p>;
}

const root = document.createElement("div");
document.body.append(root);
createRoot(root).render(<App />);

In an action, the same helper returns the user who triggered the run (its AUTH_TOKEN is the caller's token).

GET /api/v1/user/access

GET ${process.env.AUTH_URL}/api/v1/user/access[?resource_type=projects|tunnels|data|mcp], same header. Returns { status: "success", data } where data holds:

KeyContent
principalThe resolved principal.
userid, username, slug?, name?, email?, email_verified?, type, status, plan?.
billingPlan, quotas and usage.
groups[{ id, name, inherited_roles }].
global_permissionsPlatform-wide permission strings.
resources.projects[{ repository: <project UUID or "*">, permissions: { read, write, admin }, branches: { allow, deny }, tags: { allow, deny } }].
resources.tunnels[{ host_pattern, can_register, can_connect }].
resources.data{ <instance UUID>: { engine, read, write, delete? } }.

Every entry of resources.projects, resources.tunnels and resources.data also carries the quota flags sponsored, frozen, freeze_reason (count, storage or null) and transfer_exhausted. resource_type empties the other resource lists. Use it to gate UI (for example hide write actions unless resources.data[id].write && !resources.data[id].frozen: a frozen instance is read-only while write stays true); the platform enforces the same rules server side regardless.

Token hygiene

AUTH_TOKEN grants everything the user can do. Send it only to process.env.AUTH_URL, other FullStacked Cloud services, the tunnels hub (inside tunnel.register) or a backend of yours that validates it (see Runtime Sign-In & BYOA). Never log it, render it, write it to a file or commit it.

Endpoints

GET /api/v1/me

Profile of the bearer's principal: identity only, never grants or resources. BYOA principals add authority_id and omit email_verified, plan and plan_name.

  • Service: auth (https://auth.fullstacked.cloud)
  • Auth: Bearer fsc_* (AUTH_TOKEN)
  • Aliases: /v1/me
  • In project code: ${process.env.AUTH_URL}/api/v1/me
  • Pitfall: A relative fetch("/api/v1/me") reads the project's own static files: always prefix process.env.AUTH_URL.

Response:

json
{
  "status": "success",
  "data": {
    "id": "usr_0123456789abcdef01234567",
    "username": "alice",
    "slug": "alice",
    "name": "Alice",
    "email": "[email protected]",
    "email_verified": true,
    "avatar_url": null,
    "type": "user",
    "status": "active",
    "plan": "base",
    "plan_name": "Base",
    "has_run_client_gui": true,
    "restricted": false,
    "created_at": "2026-09-20T14:32:00.000Z"
  }
}

indexTs:

ts
const res = await fetch(`${process.env.AUTH_URL}/api/v1/me`, {
    headers: { Authorization: `Bearer ${process.env.AUTH_TOKEN}` },
});
const { data: user } = await res.json();

curl:

bash
curl -sS -H "Authorization: Bearer $AUTH_TOKEN" "https://auth.fullstacked.cloud/api/v1/me"

GET /api/v1/user/access

Effective access of the caller: principal, user, billing, groups, global_permissions and resources (projects, tunnels, data, mcp). Narrow with ?resource_type=projects|tunnels|data|mcp.

  • Service: auth (https://auth.fullstacked.cloud)
  • Auth: Bearer fsc_* (AUTH_TOKEN)
  • In project code: ${process.env.AUTH_URL}/api/v1/user/access

curl:

bash
curl -sS -H "Authorization: Bearer $AUTH_TOKEN" "https://auth.fullstacked.cloud/api/v1/user/access?resource_type=projects"