GNL 0.17: erken aşamada ve hızlı ilerliyor — API'ler sürümden sürüme değişebilirBize katıl

GNL
Dokümanlar menüsü
Enterprise@gnldev/auth-ee

Denetim kaydı (audit)

Kimin sorduğunu, ne aldığını ve nedenini kaydeder — her istek için bir satır, sağlayıcının onDecision kancasından console'a veya journal-destekli kalıcı sink'e (createJournalAuditSink) yazılır; yanında Studio'nun kendi yönetim-eylemi günlüğü.

Ne işe yarar / ne zaman kullanılır#

Uyumluluk (SOC2/ISO benzeri) veya sadece "kim ne zaman ne yaptı" sorusuna güvenilir cevap gerektiğinde iki ayrı denetim katmanı devreye girer. Birincisi @gnldev/auth-ee'nin AuditSink'i: host'un bildirdiği her istek için bir satır — kim sordu (ve ne tür bir çağıran), sonunda hangi cevabı aldı, neden. createEnterpriseAuth içine gömülüdür, satırları elle yazmazsınız. İkincisi Studio'nun kendi yönetim-eylemi günlüğüdür: organizasyon/kullanıcı oluşturma-silme, policy güncelleme, run purge, agent promote gibi YAZMA eylemlerini GET /audit ile geriye okunabilir tutar.

Kritik garanti: aktör alanı istemcinin iddia ettiği bir şey değil, doğrulanmış kimlikten (Principal.id) türetilir — paylaşımlı bir token arkasında birden çok kişi çalışıyorsa x-gnl-actor header'ı ile kişi bazlı iz bırakılabilir, ama hiçbir istemci başka birinin kimliğini taklit edip kayda "ben yaptım değil" diyemez.

Kurulum / import#

paket
import { createEnterpriseAuth, createJournalAuditSink, consoleAuditSink } from '@gnldev/auth-ee';
import type { AuditSink, AuditEvent } from '@gnldev/auth-ee';
import { toJournal } from '@gnldev/durable';

@gnldev/auth-ee paralı/lisanslı bir pakettir. Journal-destekli kalıcı sink için storage'ın run journal'ı toJournal (@gnldev/durable) ile sarılır.

Adım adım kullanım#

1. Kalıcı audit istiyorsanız createJournalAuditSink ile bir sink oluşturup createEnterpriseAuth'a audit seçeneği olarak verin — verilmezse varsayılan uçucu consoleAuditSink kullanılır (yalnız konsola yazar, kalıcı değildir):

src/index.ts
const auth = createEnterpriseAuth({
  licenseKey,
  userStore,
  audit: createJournalAuditSink(toJournal(storage)), // durable: decisions are written to the journal
  fallback,
});

Sağlayıcı sink'e authorize'dan değil, onDecision kancasından (@gnldev/auth) yazar. authorize, host'un sahiplik, organizasyon ve kaynak kapılarından ÖNCE cevap verir; yani onun kararı çağıranın aldığı cevap değildir. Host, istek cevaplandıktan sonra son cevabı onDecision'a bildirir.

Bir satır ne söyler: allowed, çağıranın ALDIĞI cevaptır. Başkasına ait bir koşum, varlığını gizlemek için 404 ile cevaplandıysa satır allowed: false, reason: ownership, status: 404 olur; gerçekten var olmayan bir koşum ise allowed: true, status: 404. reason şunlardan biridir: unauthenticated, rbac, ownership, organization, resource, policy. kind ise subject, operator, application veya unnamed — adı ops olan bir son kullanıcıyla ops adlı operatör farklı satırlar yazar.

Hangi kapılar satır yazar: @gnldev/server (createRestApi, bağlı surface'ler dahil) kapıdan geçen her isteği kaydeder; @gnldev/studio her isteği ve yalnız-personel reddini kaydeder. Tek başına chat ve AG-UI route'ları (createChatRoute, createAguiRoute) yalnızca onDecision: (d) => auth.onDecision?.(d) verilirse kaydeder. @gnldev/mcp'nin createMcpServer'ı kayıt YAZMAZ — kancası yoktur; gerekiyorsa kaydı allowTool içinde yazın. authorize'ı kendisi çağırıp onDecision'ı hiç çağırmayan bir host hiçbir şey kaydetmez.

chat route — kimin sorduğunu kaydetmek
import { createChatRoute } from '@gnldev/chat-adapter';
import { roleAuth } from '@gnldev/auth';

const auth = roleAuth({ endUsers: { secret: process.env.GNL_END_USER_SECRET!, orgId: 'acme' } })!;

const chat = createChatRoute(config, {
  identify: (req) => auth.authenticate(req),
  onDecision: (d) => auth.onDecision?.(d), // @gnldev/auth-ee's provider writes its audit trail here
});

Sink hata atarsa hazır host'lar cevap yerine 500 decision_not_recorded döner. Satır, istek cevaplandıktan sonra yazılır; yani iş çoğu zaman çoktan yapılmıştır: ajan koşumu modeli çağırmış ve turu kaydetmiştir (aynı runId, workKey, Idempotency-Key veya chat mesaj id'siyle tekrar denemek, modeli ikinci kez çağırmadan aynı sonucu döndürür). İstisna Studio'nun POST /users ucudur: token hiç teslim edilmediği için oluşturulan kullanıcı geri silinir (rolledBack: true).

2. Yazılan kararları geriye okumak için readJournalAudit kullanılır (en yeni önce döner):

okuma
import { readJournalAudit } from '@gnldev/auth-ee';

const events = await readJournalAudit(toJournal(storage));
// [{ ts, principalId, kind, orgId, path, method, action, permission, allowed, reason, detail, status }, ...]

3. Kurcalama kanıtı için sink'e en az 32 baytlık bir secret verin (MIN_AUDIT_SECRET_BYTES; boş veya kısa anahtar hata fırlatır). Her sink örneği kendi sırası olan bir yazıcıdır (seq 0'dan başlar); her satırın MAC'i alanlarını, journal anahtarını, yazıcısını ve seq'ini kapsar. verifyJournalAudit(journal, secret) tampered (değiştirilmiş, sahte, kopyalanmış veya taşınmış satırlar) ve gaps (bir yazıcının sırasının ortasından silinmiş satır) döner:

kurcalama kanıtı
import { createJournalAuditSink, verifyJournalAudit } from '@gnldev/auth-ee';

const journal = toJournal(storage);
const secret = process.env.AUDIT_SECRET!; // at least 32 bytes (MIN_AUDIT_SECRET_BYTES)
const audit = createJournalAuditSink(journal, { secret });

const { tampered, gaps } = await verifyJournalAudit(journal, secret);

Gösteremediği şey: bir yazıcının SON satırlarının ya da bütün satırlarının silinmesi — silinen kuyruktan sonra eksikliği gösterecek bir satır yoktur ve hiçbir MAC bunu journal içinde çözemez. Her yazıcının son seq'ini başka bir yere (ayrı bir log, bir SIEM) gönderip karşılaştırın. Başarısız bir yazma da boşluk bırakır; yani boşluk silme kanıtı değil, incelenecek bir iştir.

4. Bu, Studio'nun kendi yönetim-eylemi günlüğünden AYRIDIR. Studio, her başarılı YAZMA eylemini (ör. org.create, policy.update, run.purge, agent.promote) kendi __audit__ log'una düşürür ve organizasyon kapsamlı, filtrelenebilir bir uçtan sunar:

okuma — Studio
GET /audit?limit=200&action=policy.update&q=acme
// -> { items: [{ id, at, actor, action, target, org?, detail? }, ...] } (newest first)

Kimliğe bağlı (org'lu) bir principal yalnız kendi organizasyonunun kayıtlarını görür (?org= yok sayılır); bağsız (operatör) kimlik ?org= ile isteğe bağlı süzebilir.

API referansı#

fncreateJournalAuditSink

(journal: JournalLike, opts?: { secret?: string }) => AuditSink. Her satırı __eeaudit__:<ts>-<writer>-<seq> anahtarıyla journal'a yazan kalıcı sink. secret (en az 32 bayt) verilirse her satır bir MAC taşır.

fnconsoleAuditSink

() => AuditSink. Konsola yazan uçucu varsayılan sink (audit verilmezse kullanılır).

fnreadJournalAudit

(journal: JournalLike, orgId?: string, opts?: { secret?: string }) => Promise<AuditEvent[]>. Tüm satırları en yeni önce döner (listKeys gerektirir). orgId verilirse yalnız o organizasyonun satırları; secret verilirse her satır integrity: 'ok' | 'tampered' ile işaretlenir.

fnverifyJournalAudit

(journal: JournalLike, secret: string) => Promise<AuditVerification>. Tüm izi denetler ve { total, ok, tampered, gaps } döner. Bir yazıcının silinmiş son satırlarını tespit etmez.

constMIN_AUDIT_SECRET_BYTES

32 — kabul edilen en kısa MAC anahtarı (bayt); boş veya daha kısası hata fırlatır.

fnrecordDecision

(sink: AuditSink, decision: AccessDecision) => bir isteğin sonucunu sink'e yazar — createEnterpriseAuth'un onDecision'ı bunu çağırır.

typeAuditSink

write(event: AuditEvent): void | Promise<void> sözleşmesi — özel sink (DB/SIEM) yazmak için implemente edilir.

typeAuditEvent

{ ts, principalId?, kind, orgId?, path, method, action: "read"|"write", permission?, allowed, reason?, detail?, status?, writer?, seq?, mac? }.

constAUDIT_PRE

journal anahtar öneki: '__eeaudit__:' — createJournalAuditSink/readJournalAudit bunu paylaşır.

Dikkat
Bu özellik @gnldev/auth-ee içindedir ve geçerli bir lisans (licenseKey) gerektirir. Lisansın açmadığı bir audit sink'i ATILMAZ: kayıt yazmaya devam eder ve audit'in lisanssız olduğunu bir uyarı söyler — izi sessizce kaybetmek daha kötü olurdu. failClosed: true ile aynı kurulum boot'ta durur.
Not
consoleAuditSink uçucudur (yalnız stdout) — kalıcı/geriye-okunabilir kayıt için mutlaka createJournalAuditSink kullanın. readJournalAudit, journal listKeys desteklemiyorsa (ör. bazı özel adaptörler) boş dizi döner.
İlişkili
Yetki kararlarını üreten rol/izin katmanı için rbac, lisans doğrulama için signed-license, sürüm/promote olaylarının kendi audit izi için agent-versioning ve veri-güdümlü kural değişikliklerinin audit izi için guard-policy sayfalarına bakın.