Dokümanlar menüsü
Çok organizasyonluluk (organizasyon izolasyonu)
Her isteği bir organizasyonun izole journal'ına indirir (withOrg); run'lar, memory ve exactly-once garantileri organizasyon başına ayrışır, kimliğe bağlı organizasyon header'ı ezer (uyuşmazlıkta 403).
Ne işe yarar / ne zaman kullanılır#
Tek bir GNL dağıtımıyla birden çok müşteriye (organizasyona) hizmet vermek istediğinde — ör. bir SaaS ürününde her müşterinin run geçmişi, memory'si ve kullanım/bütçe sayaçları diğerlerinden tamamen ayrı olmalı. org opsiyonu açıldığında her istek, withOrg ile kapsamlanmış ayrı bir journal'a iner; organizasyonlar birbirinin anahtarlarını göremez, aynı runId farklı organizasyonlarda bağımsızdır.
Kimliğe bağlı organizasyon (Cred.orgId ile verilmiş, ör. bir viewer token'ı) x-gnl-org header'ını her zaman ezer: o kimlik başka bir organizasyon istese bile 403 döner — böylece bir token asla kendi organizasyonu dışına sızamaz.
Kurulum / import#
org ve OrgOptions @gnldev/server'ın createRestApi seçenekleridir; withOrg alttan @gnldev/durable'dan gelir. Kimliğe bağlı organizasyon (Cred.orgId) için EE auth (@gnldev/auth-ee) veya ücretsiz roleAuth'un orgId alanı kullanılır.
npm install @gnldev/server @gnldev/durable # @gnldev/auth-ee arrives separately, under licenseimport { SqliteStorage } from '@gnldev/durable/sqlite';
import { createRestApi } from '@gnldev/server';
import { createEnterpriseAuth } from '@gnldev/auth-ee';
import { roleAuth } from '@gnldev/auth';
const storage = new SqliteStorage('runs.db');
const config = { storage, agents: { starwars: { model, system: SYSTEM, tools } } };
// The 'acme-viewer' token carries an IDENTITY-BOUND organization (orgId) → it only ever sees
// acme's data; asking for another org returns 403. admin is global and can act in any
// organization via the x-gnl-org header.
const auth = createEnterpriseAuth({
licenseKey: process.env.GNL_LICENSE_KEY,
publicKey: process.env.GNL_EE_PUBLIC_KEY,
failClosed: true,
fallback: roleAuth({
admin: { token: ADMIN_TOKEN, user: 'ops' },
viewer: { token: ACME_TOKEN, orgId: 'acme' }, // demo viewer bound to an organization
}),
});Adım adım kullanım#
createRestApi'ye org: {} vermek yeterli — organizasyon varsayılan olarak x-gnl-org header'ından çözülür (OrgOptions.resolve ile özelleştirilebilir). Organizasyon başına registry lazy kurulur ve önbelleklenir.
app.route('/api', createRestApi(config, {
title: 'SWAPI Pro',
auth,
org: {}, // opt-in: every request lands in its organization's journal (withOrg)
budgets: {}, // optional — per-organization budget/quota (separate doc: budget-quota)
}));Global (bootstrap) kimlik, header ile istediği organizasyonda çalışabilir:
# admin: acts in any organization via the x-gnl-org header
curl -s -X POST http://localhost:3002/api/agents/starwars/run \
-H 'content-type: application/json' -H 'authorization: Bearer ${ADMIN_TOKEN}' -H 'x-gnl-org: acme' \
-d '{"runId":"demo-1","prompt":"Who is Darth Vader?"}'Organizasyona bağlı bir kimlik (viewer) header vermese bile kendi organizasyonuna düşer; farklı bir organizasyon istemeye çalışırsa 403 alır:
# organization-BOUND viewer: scoped to acme even without the header
curl -s http://localhost:3002/api/usage -H 'authorization: Bearer ${ACME_TOKEN}'
# the same viewer asking for another organization (x-gnl-org: globex) → 403 org mismatch
curl -s http://localhost:3002/api/runs -H 'authorization: Bearer ${ACME_TOKEN}' -H 'x-gnl-org: globex'
# -> 403 "organization mismatch: identity is bound to organization 'acme'"Altta yatan izolasyon mekanizması doğrudan da kullanılabilir — journal'ı elle bir organizasyona kapsamla:
import { withOrg } from '@gnldev/durable';
// createRestApi does this AUTOMATICALLY with opt-in org, but you can also use it directly:
// EVERY key in the journal is written/read under an `org:<orgId>:` prefix — runs, memory,
// queue and cache included, so EVERYTHING is isolated (same Journal interface; exactly-once
// is inherited for free).
const scoped = withOrg(baseJournal, 'acme');
// scoped.listRuns() returns ONLY acme's runs (with the prefix stripped).API referansı#
withOrgBir journal'ı organizasyona kapsamlar (@gnldev/durable): dönen journal aynı arayüzü uygular (get/put/putIfAbsent/listKeys/deletePrefix/readRun/listRuns varsa köprülenir), tüm anahtarlar `org:<orgId>:` önekiyle taşınır ve okurken önek soyulur.
RestApiOptions.orgcreateRestApi seçeneği (@gnldev/server): opt-in çok organizasyonluluk — verilirse her istek withOrg ile kapsamlanmış journal görür, çözülen organizasyon requestContext'e enjekte edilir.
OrgOptions{ resolve?: (c) => orgId, required?: boolean } — resolve verilmezse x-gnl-org header'ı okunur; required true ise organizasyonsuz istek 400 alır (false ise paylaşılan alanda çalışır).
principalOf@gnldev/auth: Hono Context'inden doğrulanmış Principal'ı okur; Principal.orgId burada gelir ve scope() içinde bağlı organizasyonu belirler.
Cred.orgIdroleAuth (@gnldev/auth) kimlik bilgisinde (Cred) opsiyonel alan: verilirse o rolün kimliği bir organizasyona bağlanır — istek header'ından bağımsız olarak her zaman o organizasyona kapsamlanır, farklı organizasyon istenirse 403.
@gnldev/auth-ee ile imzalı lisans şarttır (bkz. İmzalı lisans). Kimliğe bağlı organizasyon için EE auth zorunlu değildir; ücretsiz roleAuth'un Cred.orgId alanıyla da bağlanabilir — ama org seçeneğinin kendisi @gnldev/server'da serbesttir, paralı olan tam kimlik/rol/lisans zinciridir.createGnl çağrısı yapmaz. Bütçe/kota da organizasyon bazında ayrışır; ayrıntı için Bütçe & kota dokümanına bakın.