Panonun yapabildiği her şey bir JSON REST çağrısıdır — pano yalnızca bu API’nin bir istemcisidir. Onu betiklenmiş kurulum, zamanlanmış yeniden indeksleme ya da izleme için kullanın.
Temel URL http://localhost:3444/api
Kimlik doğrulama
Her istek şu üç kimlik bilgisinden birini taşır:
| Nedir | Kim kullanır | |
|---|---|---|
| Bir oturum çerezi | POST /api/auth/login tarafından ayarlanan contextator_session |
Pano, ve bir tarayıcıyı süren her şey |
Authorization: Bearer ctxk_… |
Bir hesabın kendi API token’ı — adlandırılmış, rota alt kümesine ve opsiyonel olarak bir projeye kapsanmış, kendi başına iptal edilebilir | Bir hesabın yetkisinin yalnızca bir kısmını taşıması gereken betikler ve CI job’ları — aşağıda API tokenları’na bakın |
Authorization: Bearer $ADMIN_TOKEN |
root izinleriyle makine erişimi |
Örneğin tamamına ihtiyaç duyan betikler, CI, cron |
export API=http://localhost:3444/api
export TOKEN=your-admin-token
curl -s $API/projects -H "Authorization: Bearer $TOKEN" | jq
Bu ayrımdan beş şey çıkar:
- Ne yapabileceğiniz kim olduğunuza bağlıdır. Bir oturum bir hesaba bağlıdır, bu yüzden onun örnek
rolü ve proje başına üyelikleri her çağrıya karar verir — bkz.
Hesaplar ve İzinler.
ADMIN_TOKENher zamanrootgibi davranır. - Bir API token’ı, onu üreten hesaptan asla daha fazlasını yapamaz. Kapsamı, o hesabın kendi rolü ve üyeliğinden sonra denetlenir, bu yüzden onu daraltmak — bir projeye, ya da bir avuç rotaya — yalnızca yetkiyi azaltabilir, asla artıramaz.
- Bir şeyi değiştiren bir çerez isteği bu siteden gelmelidir. Sunucu
Sec-Fetch-Site’ı denetler, yoksaOriginveReferer’a düşer, aksi halde403 csrf_blockedyanıtlar. Bearer istekleri muaftır: hiçbir çevresel kimlik bilgisi taşımazlar, bu yüzden ikisini de göndermeyencurlve CI değişmeden çalışır. - İki bearer token’dan hiçbiri, bu rotalar söz konusu olduğunda, oturum açmış bir hesap sayılmaz.
Sizin hesabınızla ilgili endpoint’ler (
/api/auth/password,/api/auth/sessions) bir oturum gerektirir — bu denetim, tam olarak o rotaya izin verecek şekilde kapsanmış bir token dahil,ADMIN_TOKEN’ı ve kapsamlı bir API token’ını aynı şekilde403 token_has_no_accountile reddeder. Yalnızca bir oturum çerezi taşıyan bir tarayıcı onlara ulaşabilir. - Ulaşamadığınız bir proje
403değil404yanıtlar, var olmayan bir projeyle aynı şekilde, bu yüzden id’ler yoklanamaz. Ulaşabildiğiniz ama bu şekilde kullanamayacağınız bir rota403yanıtlar.
GET /api/health, GET /api/setup/status, POST /api/setup, POST /api/auth/login,
GET /api/auth/oidc/login ve GET /api/auth/oidc/callback, hiçbir kimlik bilgisi olmadan ulaşılabilen
tek endpoint’lerdir — son ikisi, tarayıcı yönlendirmeyi istediğinde henüz bir oturumu olmadığı için, ve
sağlayıcı tarayıcıyı kodla birlikte, bir yönlendirmeyle callback’e geri gönderdiği için — sağlayıcının
kendisinden sunucudan sunucuya bir çağrı değil. Zaten oturum açmış bir hesabı aynı sağlayıcıya
bağlamak (POST /api/auth/oidc/link, DELETE /api/auth/oidc/link — bkz. Tek Oturum
Açma) farklıdır: çağıranın kendi
oturumuna ihtiyaç duyar, tıpkı başka her self-servis hesap endpoint’i gibi.
Sağlık
curl -s $API/health | jq
{
"ok": true,
"version": "0.2.0",
"uptimeSec": 3610,
"db": "up",
"embeddings": {
"id": "local:Xenova/multilingual-e5-small:fp32:\"query: \"+\"passage: \"",
"provider": "local", "model": "Xenova/multilingual-e5-small",
"dimensions": 384, "dtype": "fp32", "ready": true
},
"sessions": { "total": 2, "streamable": 2, "sse": 0 },
"allowedDocRoots": ["/docs"],
"dataDir": "/data",
"secretKeyConfigured": true,
"uploads": { "maxFileBytes": 52428800, "maxFilesPerRequest": 500, "maxArchiveBytes": 268435456 }
}
İyi bir canlılık probu: ok === true && db === "up". İyi bir hazırlık probu: ayrıca
embeddings.ready. Yanıt, veritabanına ulaşılamadığında aynı gövdeyle 503 yanıtlar, aksi halde
200 — bir izleyicinin bunun için ikinci bir denetime ihtiyacı yoktur.
embeddings ayrıca modelin girdi penceresini ve CHUNK_MAX_TOKENS’ın onun içine sığıp sığmadığını
taşır, bu yüzden parçaları sessizce kesecek bir yanlış yapılandırma arama kalitesinde ortaya çıkmadan
önce burada görünür — bkz. Yapılandırma.
Anonim bir çağıran daha kısa bir cevap alır — ok, version, db, authRequired ve needsSetup —
çünkü geri kalanı makineyi anlatır. Bir izleyicinin izlediği alanlar her iki biçimde de vardır.
Kurulum ve oturum açma
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/setup/status |
{ needsSetup } — bu örneğin hiç hesabı yokken true. Herkese açık |
POST /api/setup |
{ code, username, displayName?, email?, password } → 201 { user }, ve oturum çerezi. İlk root hesabını oluşturur. Yanlış kodda 403 invalid_setup_code; bir hesap var olduktan sonra 409. Herkese açık |
POST /api/auth/login |
{ username, password } → { user, mustChangePassword } ve çerez. Hem yanlış bir parola hem de bilinmeyen bir kullanıcı adı için 401 invalid_credentials, 403 account_disabled, hız sınırlandığında Retry-After ile 429. Herkese açık |
POST /api/auth/logout |
Bu oturumu iptal eder ve çerezi temizler → 204. İdempotenttir |
GET /api/auth/me |
Oturum açmış hesap, rolü, authKind (session|token|apiToken — bir pano oturumu, ADMIN_TOKEN ya da bir API token’ı), ve bir member için projects içindeki proje başına rolleri. Ayrıca oidc ({ enabled, buttonLabel }), canLinkOidc (root için false) ve federatedProviders — aşağıda Tek oturum açma’ya bakın |
POST /api/auth/password |
{ currentPassword, newPassword } → 204. mustChangePassword’ü temizler ve bu hesabın diğer oturumlarını iptal eder |
GET /api/auth/sessions |
Kendi canlı oturumlarınız: her biri ne zaman başladı, en son ne zaman görüldü, user agent’ı ve IP’si, ve hangisi current |
DELETE /api/auth/sessions?scope=others|all |
Onları sonlandırır → 204. Varsayılan others’tır; all sizi de çıkarır |
# sign in and keep the cookie in a jar, then use it like a browser would
curl -s -c jar -X POST $API/auth/login -H 'content-type: application/json' \
-d '{"username":"alice","password":"…"}' | jq
curl -s -b jar $API/auth/me | jq
Tek oturum açma
Yalnızca bu örnekte bir OIDC sağlayıcısı yapılandırılmışsa bulunur — kimin bağlayabileceği ve root’un neden bağlayamadığı için bkz. Tek Oturum Açma.
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/auth/oidc/login |
Oturum açmak için kimlik sağlayıcıya yönlendirir. Herkese açık — tarayıcının henüz bir oturumu yoktur |
GET /api/auth/oidc/callback |
Kimlik sağlayıcı tarayıcıyı kodla birlikte, bir yönlendirmeyle buraya geri gönderir — sağlayıcıdan sunucudan sunucuya bir çağrı değil. Herkese açıktır çünkü bir oturum açma henüz bir oturum olmadan gelir; bir bağlama ise onu başlatan oturumla, yönlendirmenin kendi çerezinin taşıdığı şekilde gelir. Bir root hesabını oturum açtırmayı reddeder (root_local_only), bağlantısı bir root’a yükseltilmeden önce yapılmış olsa bile — rol, bağlantının yapıldığı andan varsayılmaz, callback anında hesaptan yeniden okunur |
POST /api/auth/oidc/link |
Zaten oturum açmış bir hesap için self-servis: aynı yönlendirmeyi başlatır, ama oturum açmak yerine sağlayıcıyı bu hesaba bağlamak için. Bir POST olduğu için same-site denetimi geçerlidir. → gidilecek 200 { url }. Root için reddedilir (403 root_local_only) — root asla SSO üzerinden oturum açmaz |
DELETE /api/auth/oidc/link |
Çağıranın kendi bağlı kimliğini kaldırır → 204. Kendi hesabınıza kapsanmıştır; başka bir hesabın bağlantısı üzerinde bir admin görünümü yoktur. Root için reddedilmez — root’a yükseltilmeden önce yapılmış bir bağlantı kaldırılana kadar yerinde kalır, ve bunu yapan yol budur. Ayrıca hesabın tuttuğu her oturumu ve API token’ını, bu çağrıyı yapanı da dahil, ve — aynı transaction içinde — hesaba verilmiş her MCP OAuth erişim ve yenileme token’ını iptal eder, bu yüzden onunla oturum açmış bir MCP istemcisi yeniden yetkilendirene kadar 401 alır; diğer hesaplar dokunulmadan kalır. Bağlantı kesmenin audit olayı sayıyı detail.revokedMcpCredentials olarak kaydeder. 409 last_sign_in_method — hiçbir şey kaldırılmaz, hiçbir şey iptal edilmez — hesabın yerel bir parolası yoksa ve bu bağlantı oturum açmasının tek yoluysa; bir admin önce POST /api/users/:id/password ile bir parola ayarlar, ve bağlantı kesme ardından başarılı olur |
Hesaplar
/api/users/*, admin ya da root gerektirir. Yalnızca bir root hesabı başka bir root hesabı
oluşturabilir, değiştirebilir ya da silebilir. PATCH /api/users/:id, hâlâ bağlı bir SSO kimliği olan
bir hesapta role: "root" ayarlamak istendiğinde 409 root_requires_unlink ile reddeder — önce onu
yukarıdaki DELETE /api/auth/oidc/link ile bağlantısını kesin.
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/users |
Her hesap: rol, active anahtarı, mustChangePassword, proje sayısı, son oturum açma, ve activeSessionCount |
POST /api/users |
{ username, displayName?, email?, role?, password?, mustChangePassword? } → 201 { user, temporaryPassword }. role varsayılan olarak member, mustChangePassword varsayılan olarak true; password’ü atlayın, biri üretilir ve bir kez döndürülür |
GET /api/users/:id |
{ user, activeSessionCount } |
PATCH /api/users/:id |
{ displayName?, email?, role?, isActive? } → güncellenmiş hesap. Bir düşürme ya da devre dışı bırakma o hesabın oturumlarını da sona erdirir |
POST /api/users/:id/password |
{ password? } → { temporaryPassword }. Sonraki oturum açmada bir değişikliği zorlar ve o hesabın oturumlarını sona erdirir |
DELETE /api/users/:id |
Hesabı, oturumlarını ve üyeliklerini siler → 204 |
DELETE /api/users/:id/sessions |
O hesabı her yerde oturumdan çıkarır → 204 |
409, son aktif root hesabını silinmeye, düşürülmeye ve devre dışı bırakılmaya karşı korur; 403, bir
admin’in bir root hesabına uzanmasına ve herkesin kendi rolüne ya da active anahtarına uzanmasına karşı
korur.
# hand someone a new temporary password without touching the dashboard
ID=$(curl -s $API/users -H "Authorization: Bearer $TOKEN" | jq -r '.[] | select(.username=="alice") | .id')
curl -s -X POST $API/users/$ID/password -H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{}' | jq -r .temporaryPassword
API tokenları
/api/tokens* yalnızca bir oturum gerektirir — her hesap kendi tokenlarını yönetir, ve başkasının
tokenları üzerinde bir admin görünümü yoktur. Bir betiğin ya da CI job’unun ADMIN_TOKEN’dan daha dar
bir kimlik bilgisi alma yolu budur: adlandırın, opsiyonel olarak bir projeye ve bir son kullanma
tarihine sabitleyin, ve tam olarak hangi rotaları çağırabileceğini listeleyin. Panonun kendi Account
menu → API tokens sayfası tam olarak bu endpoint’lerin bir istemcisidir — orada curl’e hiç
dokunmadan üretin, listeleyin ve iptal edin.
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/tokens |
Kendi tokenlarınız: id, name, prefix, scope, projectId, createdAt, lastUsedAt, revokedAt, expiresAt. Asla secret değil |
POST /api/tokens |
{ name?, scope, projectId?, expiresAt? } → 201 { token, secret }. scope, "POST /api/projects/:id/reindex" gibi bir ya da daha fazla "<METHOD> <route template>" dizesidir. Üretme yalnızca bu biçimi ve, verildiyse, expiresAt’in gelecekte olduğunu denetler — bir girdiyi kendi rolünüze karşı, ya da projectId’yi kendi projelerinize karşı denetlemez. Token’ı sınırlı tutan şey, her isteğin canlı rolünüzü ve proje erişiminizi çağrı anında yeniden denetlemesidir. secret yalnız bu yanıtta döner, bir daha asla döndürülmez. Üretim çalışabilmeden önce oturumunuz iptal edildiyse 401 session_revoked |
DELETE /api/tokens/:tokenId |
Kendi tokenlarınızdan birini iptal eder → 204. Bir sonraki istekte hemen etkili olur |
# mint a token that can only reindex one project — /api/tokens needs a session, not
# ADMIN_TOKEN, so sign in first (see "Setup and sign-in" above) and reuse the cookie jar.
# a cookie-authenticated request that changes something needs Sec-Fetch-Site too — see the
# CSRF rule above — curl never sends it on its own, unlike a real page's fetch would
curl -s -X POST $API/tokens -b jar \
-H 'content-type: application/json' -H 'Sec-Fetch-Site: same-origin' \
-d '{"name":"ci-reindex","scope":["POST /api/projects/:id/reindex"],"projectId":"'"$P"'"}' | jq
projectId içinde adlandırılan projeyi silmek, erişimini genişletmek yerine token’ı da siler.
Proje üyeleri
Bir projeye kim ulaşır. root, admin ve ADMIN_TOKEN her projeye bir üyelik olmadan ulaşır, bu
yüzden burada yalnızca member hesapları görünür.
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/projects/:id/members |
Projenin üyeleri ve rolleri. Projenin herhangi bir üyesi bunu okuyabilir |
PUT /api/projects/:id/members/:userId |
{ "role": "viewer" | "editor" } → üye. Üyeliği oluşturur ya da değiştirir (root/admin) |
DELETE /api/projects/:id/members/:userId |
Erişimi iptal eder → 204 (root/admin) |
Bir member hesabının sonra görebileceği bir projeye üye eklemek:
curl -s -X PUT $API/projects/$P/members/$ID -H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"role":"editor"}' | jq
Hedef bir admin ya da root hesabıysa 400 — zaten erişimleri vardır, bu yüzden bir üyelik satırı
doğru olmayan bir şey söylerdi.
Projeler
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/projects |
Sayılarla, mcpUrl ile ve canlı indeksleme job’uyla birlikte tüm projeler |
POST /api/projects |
{ "name": "demo", "rootPath": "/docs/demo", "index": true } → 201. rootPath opsiyoneldir ve ilk yerel kaynağı oluşturur |
GET /api/projects/:id/status |
Bir proje, artı job’u |
POST /api/projects/:id/reindex?force=true |
Bir çalışmayı kuyruğa alır → 202 { job } |
GET /api/projects/:id/runs |
En yeniden en eskiye son indeksleme çalışmaları |
GET /api/projects/:id/search?q=…&limit=…&source=…&path_prefix=…&version=… |
Projenin search_docs aracının çalıştırdığı aynı arama, JSON olarak: { query, limit, source, pathPrefix, version, belowFloor, scoreFloor, hits: [{ score, fusedScore, denseRank, lexicalRank, path, title, headingPath, chunkIndex, content, contextBefore, contextAfter }] }. score cosine similarity’dir; yalnız gösterilir, sıralamada kullanılmaz; listeyi sıralayan fusedScore’dur, ve iki rank alıntıyı aramanın hangi yarısının bulduğunu söyler (bulmayan yarı için null). belowFloor, bir ajana iyi eşleşme yok denip denmeyeceğidir — hit’ler her iki durumda da geri gelir, böylece pano nelerin gizlendiğini gösterebilir. limit 1–20’dir (varsayılan 5); source, path_prefix ve version isteğe bağlıdır. Bu projenin sahip olmadığı bir kaynak ya da sürüm için 400 invalid_request (mesaj sahip olduklarını adlandırır), projenin hiç parçası yoksa 409 not_indexed, başka bir modelle gömüldülerse 409 model_mismatch |
DELETE /api/projects/:id |
Projeyi siler → 204 (indekslerken 409) |
# create and index
curl -s -X POST $API/projects -H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"name":"handbook","rootPath":"/docs/handbook","index":true}' | jq
# re-index everything, every night
for id in $(curl -s $API/projects -H "Authorization: Bearer $TOKEN" | jq -r '.[].id'); do
curl -s -X POST "$API/projects/$id/reindex" -H "Authorization: Bearer $TOKEN" > /dev/null
done
Bir job’u izlemek
GET /api/projects, canlı bir çalışması olan her proje için bir job içerir:
{ "phase": "embedding", "filesTotal": 84, "filesDone": 31, "filesSkipped": 22,
"filesRemoved": 0, "chunksDone": 96,
"sources": [{ "name": "handbook", "type": "local", "status": "synced" }],
"queue": null }
phase, queued, syncing, scanning, embedding, finalizing, done, error’dan biridir.
Kuyruktaki bir job queue.aheadProjectName taşır.
MCP erişimi
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/projects/:id/mcp-tokens |
Projenin canlı tokenları: id, name, prefix, createdAt, lastUsedAt. Secret’ın kendisi asla döndürülmez |
POST /api/projects/:id/mcp-tokens |
{ "name": "claude-code" } → 201 { token, secret }. secret token’ın okunabilir olduğu tek andır |
DELETE /api/projects/:id/mcp-tokens/:tokenId |
İptal eder → 204, projenin açık MCP oturumlarını kapatır |
PATCH /api/projects/:id/mcp-auth |
{ "mode": "open" | "token" | "account" } → { "mcpAuth" }. open’dan uzaklaşmak da projenin açık oturumlarını kapatır. account, çağıranın /mcp/* üzerinde OAuth 2.1 üzerinden projenin bir üyesi olmasını gerektirir; bu modda statik bir ctxm_… token reddedilir |
GET /api/projects, geçerli modu mcpAuth olarak bildirir. Bir endpoint’i kapatmak ve bir token
üretmek:
curl -s -X PATCH $API/projects/$P/mcp-auth -H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"mode":"token"}' | jq
curl -s -X POST $API/projects/$P/mcp-tokens -H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"name":"claude-code"}' | jq -r .secret
İstemci sonra bu secret’ı /mcp/<project-name> üzerinde Authorization: Bearer ctxm_… olarak gönderir
— bkz. Yapay Zekâ İstemcilerini Bağlamak.
Kaynaklar
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/projects/:id/sources |
Projenin kaynakları. Secret’lar asla döndürülmez — yalnızca hasSecret |
POST /api/projects/:id/sources { type, name, label?, flavor?, config?, secret?, syncIntervalMinutes?, index? } |
Bir kaynak ekler. type, local, git, upload, notion, confluence ya da web’dir; config türe özeldir — aşağıdaki tabloya bakın. secret, yalnızca bir kimlik bilgisi kullanan türlerce alınır (git, notion, confluence); local, upload ya da web üzerinde türü adlandıran bir 400 invalid_request yanıtlar, ve secret: null (“secret yok” anlamında) her türde kabul edilir. syncIntervalMinutes, 5–43200 ya da null’dır; atlanırsa örnek varsayılanı alınır |
PATCH /api/projects/:id/sources/:sid |
Etiketi, içerik türünü, config’i ya da token’ı değiştirir ("secret": null onu kaldırır). Tür ve ad değiştirilemez |
DELETE /api/projects/:id/sources/:sid |
Onu, dokümanlarını ve dosyalarını kaldırır (indekslerken 409) |
POST /api/projects/:id/sources/:sid/sync |
Bir çalışmayı kuyruğa alır → 202 { job } |
POST /api/projects/:id/sources/:sid/test |
Bağlantı denetimi → { ok, message } |
POST /api/projects/:id/sources/:sid/webhook-secret |
Yeni bir webhook secret’ı üretir: bir git kaynağında onu rotate eder, ve bir Confluence kaynağında webhook’u açar (ya da yeniden üretir) — bkz. Push Webhook’ları |
DELETE /api/projects/:id/sources/:sid/webhook-secret |
Yalnızca Confluence: webhook’u kapatır. Teslimatlar ardından yeniden not_enabled ile reddedilir |
Bir git kaynağı eklemek:
curl -s -X POST $API/projects/$PROJECT_ID/sources \
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{
"type": "git",
"name": "api-docs",
"label": "API reference",
"config": { "url": "https://github.com/org/repo.git", "branch": "main", "subdir": "docs" },
"secret": "ghp_…",
"index": true
}' | jq
Türe göre config:
| Tür | Anahtarlar |
|---|---|
local |
path, extensions |
git |
url, branch, subdir, username, provider, extensions |
upload |
extensions |
notion |
rootIds, extensions |
confluence |
deployment (cloud | datacenter), siteUrl, accountEmail (yalnızca Cloud), spaces — bkz. Confluence |
web |
entryUrl, entryKind (auto | sitemap | llms | crawl, varsayılan auto), extensions — yalnızca herkese açık sayfalar, kimlik bilgisi yok |
Yüklemeler
| Yöntem ve yol | Açıklama |
|---|---|
POST /api/projects/:id/sources/:sid/uploads |
Bir oturum açar → { session } |
POST …/uploads/:session/files |
multipart/form-data; her parçanın filename’i kaynak içindeki yoldur. Arşivler sunucu tarafında açılır |
POST …/uploads/:session/commit?mode=add|replace |
Bekletilen dosyaları içeri taşır ve bir çalışmayı kuyruğa alır → 202 |
DELETE …/uploads/:session |
Bekletilen yüklemeyi atar |
GET /api/projects/:id/sources/:sid/files |
Bir yükleme kaynağının dosyalarını listeler |
DELETE /api/projects/:id/sources/:sid/files?path=… |
Birini siler ve yeniden indeksler |
SESSION=$(curl -s -X POST $API/projects/$P/sources/$S/uploads -H "Authorization: Bearer $TOKEN" | jq -r .session)
curl -s -X POST "$API/projects/$P/sources/$S/uploads/$SESSION/files" \
-H "Authorization: Bearer $TOKEN" -F '[email protected];filename=guides/guide.md'
curl -s -X POST "$API/projects/$P/sources/$S/uploads/$SESSION/commit?mode=add" \
-H "Authorization: Bearer $TOKEN"
Webhook’lar
| Yöntem ve yol | Açıklama |
|---|---|
POST /api/webhooks/git/:sourceId |
Bir git kaynağı için push webhook’u, bir hesap ya da ADMIN_TOKEN değil, kaynak başına secret ile kimliklendirilir |
POST /api/webhooks/confluence/:sourceId |
Confluence Data Center webhook’u, kaynak başına secret üzerinden X-Hub-Signature ile imzalanır. Kaynağın webhook’u açılana kadar 401 not_enabled |
Bkz. Push Webhook’ları.
Metrikler
curl -s http://localhost:3444/metrics -H "Authorization: Bearer $METRICS_TOKEN"
GET /metrics, kendi kimlik bilgisi kurallarına sahip ayrı, Prometheus biçimli bir endpoint’tir
(text/plain; version=0.0.4) — /api/*’ın bir parçası değildir ve varsayılan olarak herkese
açık değildir. Şeride göre indeksleme kuyruğunu, birinin çalışıp çalışmadığını, son indeksleme
çalışmasını ve işe yarayıp yaramadığını, aktöre göre aramaları, veritabanı bağlantı havuzunu ve
veritabanının kendisinin yanıt verip vermediğini bildirir.
Açık bir oturumu, ADMIN_TOKEN’ı, bir METRICS_TOKEN bearer’ını, ya da
METRICS_PUBLIC=1 iken hiçbir kimlik bilgisini kabul eder — bkz.
Yapılandırma. Veritabanı çökükken bile 200 yanıtlar,
contextator_db_up 0 ile ve veritabanı gerektiren satırlar dışarıda bırakılarak, bu yüzden bir scrape
boşluğu bir kesintinin bildirilme yolu asla olmaz. Veritabanı çökükken yalnızca veritabanı okuması
gerektirmeyen üç kimlik bilgisi çalışmaya devam eder — ADMIN_TOKEN, METRICS_TOKEN,
METRICS_PUBLIC — bir oturum çerezi bir veritabanı satırıdır ve doğrulanamaz, bu yüzden bir tane
sunan bir tarayıcı bir kesinti sırasında 500 yerine 401 alır. METRICS_TOKEN’ı bir kesinti
sırasında değil öncesinde yapılandırmanın nedeni budur.
Audit log
/api/audit, root ya da admin gerektirir — log örnek geneli olduğu için, bir proje üyeliği kime
root rolü verildiğini okumak için bir dayanak değildir.
| Yöntem ve yol | Açıklama |
|---|---|
GET /api/audit?actor&actorUser&action&project&from&to&limit&cursor |
Audit log, en yeniden en eskiye. Her filtre SQL’de uygulanır: actor, o anda kaydedildiği haliyle etikettir; actorUser, işlemi yapan hesaba karşı tam olarak eşleştirilen bir hesap id’sidir — o hesabın kendi oturumlarını ve sahip olduğu her API token’ını döndürür, tokenlar nasıl adlandırılmış olursa olsun; bir UUID olmayan bir değer 400 validation_failed yanıtlar. action, <METHOD> <rota şablonu>’dur; project, bir proje id’si ya da none’dır. from/to UTC günleridir, ve to adlandırılan günü dahil eder. limit 1–200’dür (varsayılan 50). cursor, önceki sayfanın nextCursor’ıdır — kodlanmış bir an değil her zaman bir satır id’si, bu yüzden aynı milisaniye içindeki iki olay bir sayfa sınırında birbirini asla kaybedemez; hiçbir satırı adlandırmayan bir cursor 400 yanıtlar. { events, nextCursor, filters, retentionDays } döner — son sayfada nextCursor null’dır, ve filters (farklı aktörler, eylemler, projeler, ve olayları olan hesaplar, seçiciler için) yalnızca ilk sayfada geri gelir |
curl -s "$API/audit?action=DELETE%20/api/projects/:id&limit=50" \
-H "Authorization: Bearer $TOKEN" | jq
Nasıl yazılır
Yukarıdaki durum değiştiren her istek, audit_events’te isteği yapan hesabı adlandıran bir satır
bırakır — ne yapıldığı, hangi projeye, kaynağa, token’a, üyeliğe ya da hesaba, ve ne zaman. Bir oturum
açma ve ilk hesabın oluşturulması da kaydedilir, ve bir kişinin bir konnektöre bir projeye kalıcı okuma
erişimi verdiği POST /oauth/authorize de öyle.
Her handler tarafından değil politika katmanı tarafından yazılır, bu yüzden kapsanmadan
eklenebilecek bir rota yoktur — ve katılmama seçeneği olan da yoktur. İstisnalar sekiz tanedir — bir
kaynağın test ve upload-session rotaları, üç webhook teslimatı (git, Notion, Confluence) ve üç OAuth
client-registration rotası (register, token, revoke) — her biri src/auth/policy.ts içinde kendi
gerekçesiyle adlandırılmıştır.
Bir şey oluşturan bir rota kendi yolunda hiçbir şey adlandırmaz, bu yüzden yeni nesnenin id’si yanıttan geri okunur, aynı dosyadaki sabit yollardan oluşan bir tablo üzerinden, ve yalnızca orada bulunan değer bir UUID olduğunda tutulur — “bunu kim üretti” bu yüzden log’un yanıtladığı bir sorudur, ve aynı id’yi adlandıran iptalle hizalanır.
Neyi tutmaz
Satırlar hiçbir kullanıcı içeriği taşımaz: bir soru, bir belge ve bir alıntı bu tablonun bir sütununa
asla ulaşmaz. Herhangi bir eylemin kaydedebileceği tek gövde alanları aynı politika dosyasında
adlandırılır, her biri kapalı bir değer kümesiyle sınırlıdır (mode, open/token/account’tan
biridir, ve benzeri). Bu, onu sorgu günlüğünden farklı bir kayıt tutar — o, ajanların ne sorduğunu
tutar, kendi saklama süresiyle yönetilir ve kendi proje başına anahtarına sahiptir — ikisi bilerek tek
bir tablo değildir.
Bunun ötesinde iki şey bilerek kaydedilmez. Reddedilen istekler — bir reddediş, izin matrisinin
çalışmasıdır, ve her yoklamayı loglamak tabloyu bir tarama günlüğüne dönüştürürdü; tek istisna bir
kişinin /oauth/authorize’da bir konnektörü reddetmesidir, ki bu matrisin karar vermesi değil birinin
karar vermesidir. Ve bir şeyi değiştiren ve ardından 5xx yanıtlayan bir eylem: satır yalnızca
400’ün altındaki bir yanıt için yazılır, bu yüzden commit edip sonra başarısız olan bir handler hiçbir
şey bırakmaz. Bu API’de şu anda bu şekilde biçimlenmiş hiçbir handler yok, ve SECURITY.md bunu olduğu
sınır olarak adlandırır.
Okumak
GET /api/audit ve panonun Audit log paneli (hesap menüsü, Users’ın yanında) ikisi de yalnızca
root/admin’dir. Her satır, saklandığı sütunlar yerine bir cümle olarak render edilir (“dana deleted a source from
handbook” — “dana handbook’tan bir kaynak sildi”). “Kim” iki seçicidir: Actor, kaydedildiği haliyle etikettir, ve Account, hesap
id’sidir — bu, o hesabın kendi eylemlerini sahip olduğu her API token’ınınkilerle birlikte bulur, çünkü
bir token’ın etiketi yalnızca adı ve sahibidir ve onları kendi başına toplayamaz. Bir proje o zamandan
beri silinmişse satırları hâlâ vardır: project_id bir foreign key taşımaz, bu yüzden panel adını
arayamaz, ve bunu boş bırakmak yerine satırda söyler.
Panelin filtrelediği iki sütun — actor_label, çünkü hesaptan daha uzun ömürlüdür, ve action —
0011 migration’ı ile indekslenir. 200.004 satırda, seçici bir actor filtresi indekssiz 9,7 ms’ye karşı
0,07 ms’de çalışır, bir sayfa çevirme 3 ms’dir, ve ilk sayfa 19–52 ms’dir çünkü hiçbir indeksin
yardım edemeyeceği üç DISTINCT taramasıyla filtre açılır menülerini de doldurur — filtre değişimi
başına bir kez ödenir, hiçbir zaman sayfa çevirme başına değil.
Log tamper-evident değildir — veritabanı erişimi olan herkes bir satırı kaldırabilir ve burada
hiçbir şey bunu göstermez (SECURITY.md) — ve AUDIT_LOG_RETENTION_DAYS’ten daha eski satırlar,
süresi dolmuş oturumlarla aynı çeyrek saatlik zamanlayıcıda süpürülür.
Hatalar
| Durum | Anlamı |
|---|---|
400 |
Doğrulama başarısız oldu, ya da geçersiz bir istek (bozuk yol, eksik SECRET_KEY, politikanın reddettiği bir parola) — message açıklar |
401 |
Kimlik bilgisi yok, ya da yanlış. setup_required, bu örneğin henüz hiç hesabı olmadığı anlamına gelir |
403 |
Oturum açık, ama izin yok: forbidden, csrf_blocked, password_change_required, account_disabled, token_has_no_account |
404 |
Bilinmeyen proje, kaynak ya da oturum — ve bu hesabın erişimi olmadığı her proje |
409 |
Yinelenen ad, proje indeksleniyor, kurulum zaten tamamlandı, ya da son root hesabı. DELETE /api/auth/oidc/link, hesabın yerel bir parolası olmadığında bunun yerine 409 last_sign_in_method yanıtlar — bağlantıyı kesmek onu oturum açacak bir yol bırakmazdı, bu yüzden bir admin bir tane ayarlayana kadar hiçbir şey kaldırılmaz |
429 |
Çok fazla oturum açma denemesi. Retry-After ve retryAfterSec ne kadar süreceğini söyler |
500 |
İç hata — cevap yalnızca bunu söyler; ayrıntılar loglardadır |
Tüm hatalar JSON’dur: { "error": "conflict", "message": "…" }.
Notlar
- Secret’lar hiçbir endpoint tarafından asla döndürülmez; bir kaynak yalnızca
hasSecret’ı bildirir, ve bir parola, bir geçici parolanın üretildiği o tek sefer dışında hiç döndürülmez. POST /api/projects/:id/reindex’i sık çağırmak güvenlidir: indeksleme artımlıdır.- Yetkilendirme, rotaları yöntemlerine ve biçimlerine göre kapsayan tek bir politika tablosundan sunucu tarafında kararlaştırılır, bu yüzden yeni bir endpoint var olduğu anda korunur. Panonun gizlediği şey kozmetiktir.
- API, panoyla ve MCP endpoint’leriyle aynı porta bağlıdır, bu yüzden birine ulaşabilen ötekilere de ulaşabilir (Güvenlik).