# Yapılandırma

> Tüm QueryProxy ortam değişkenleri — limitler, zaman aşımları, TLS, sırlar, SCIM, Slack, şema gezgini, metrikler, SIEM, vekiller, uygulama veritabanı, kuyruk.

- Dokümantasyon: Referans
- Güncelleme: 2026-11-04
- Kaynak: https://queryproxy.com/tr/docs/yapilandirma/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
Tüm yapılandırma ortam değişkenleriyle yapılır (yerelde `.env`, Docker'da
konteyner ortamı). Depodaki `.env.example` tam seti belgeler; bu sayfa gerçekten
değiştireceğiniz olanları açıklar.

## QueryProxy ayarları

| Değişken | Varsayılan | Anlamı |
| :-- | :-- | :-- |
| `QUERYPROXY_SELECT_DEFAULT_LIMIT` | `1000` | LIMIT'siz SELECT'lere eklenen limit |
| `QUERYPROXY_SELECT_HARD_LIMIT` | `10000` | Daha büyük LIMIT'ler bu değere indirilir |
| `QUERYPROXY_EXECUTION_TIMEOUT` | `300` | Tek bir talebin işçide çalışabileceği en uzun süre (sn). Sıfırdan büyük tam sayı saniye olmalı; başka her değer (metin, boş, `0`, negatif, ondalık, birimli değer) `300`'e düşer. Üst sınır `86400` saniyedir (24 saat): bunu aşan geçerli bir değer hata vermez, `86400`'e kırpılır. Docker entrypoint'i değeri kırptığında `[entrypoint]` uyarısı basar. Veritabanı tarafındaki ifade zaman aşımı bundan türetilir (çalıştırma zaman aşımı eksi 5 saniye, en az 1); böylece veritabanı job'dan önce durur. Bkz. [Okuma kimlik bilgileri ve zaman aşımları](#okuma-kimlik-bilgileri-ve-zaman-aşımları) |
| `QUERYPROXY_LOCK_TIMEOUT` | `5` | Hedef veritabanında bir ifadenin kilit için, veritabanı vazgeçmeden önce bekleyeceği saniye. Yalnızca rakam, 1 ile 3600 arası. Daha büyük değer `3600`'e kırpılır; başka her değer (metin, boş, `0`) `5`'e düşer |
| `QUERYPROXY_CONNECTION_SLOTS` | `2` | Bir hedef bağlantıda aynı anda kaç talebin çalışabileceği. Kalanlar `queued` durumunda bekler ve bir yuva boşalınca başlar; `0` sınırı kapatır |
| `QUERYPROXY_CONNECTION_SLOT_RETRY_DELAY` | `5` | Tüm yuvaları dolu bulan bir talebin yeniden denemeden önce bekleyeceği saniye |
| `QUERYPROXY_SCHEMA_TIMEOUT` | `5` | Şema gezgininin katalog okumasının ifade zaman aşımı, saniye. Bkz. [Şema gezgini](#şema-gezgini) |
| `QUERYPROXY_SCHEMA_CACHE_TTL` | `3600` | Bir bağlantının şemasının önbellekte kalma süresi, saniye; `0` önbelleği kapatır |
| `QUERYPROXY_SCHEMA_REFRESH_LIMIT` | `3` | Bir kullanıcının bağlantı ve dakika başına isteyebileceği elle şema yenilemesi |
| `QUERYPROXY_SCHEMA_MAX_COLUMNS` | `20000` | Tek bir şema okumasının döndürdüğü en çok sütun; daha büyük şema bir tablo sınırında kesilir |
| `QUERYPROXY_RESULT_DISK` | `local` | Sonuç dosyaları için Laravel disk adı (`s3` desteklenir; herkese açık görünürlükteki diskler reddedilir) |
| `QUERYPROXY_RESULT_TTL_DAYS` | `30` | `results:prune` sonuçları kaç gün sonra siler |
| `QUERYPROXY_SEED_DEMO` | `false` | Yalnızca Docker: ilk açılışta demo takımı oluştur (tek rastgele parola, loglara bir kez yazılır; `QUERYPROXY_SEED_DEMO_FORCE=true` olmadıkça `APP_ENV=production` iken reddedilir) |
| `QUERYPROXY_ADMIN_EMAIL` / `QUERYPROXY_ADMIN_PASSWORD` | — | Yalnızca Docker: kurulumda hiç kullanıcı yokken açılışta ilk sistem yöneticisini oluştur (parola en az 8 karakter); herhangi bir kullanıcı olduğu anda etkisizdir |
| `QUERYPROXY_ADMIN_NAME` | `Admin` | Yalnızca Docker: o ilk yöneticinin görünen adı |
| `QUERYPROXY_RUN_WORKER` | `true` | Yalnızca Docker: kuyruk işçisini konteyner içinde çalıştır; işçi kendi konteynerinde koşuyorsa `false` yapın. Helm chart'ı bunu Deployment başına ayarlar (bkz. [Kubernetes](/tr/docs/kubernetes-ve-helm/)) |
| `QUERYPROXY_RUN_SCHEDULER` | `true` | Yalnızca Docker: zamanlayıcıyı (`schedule:work`) konteyner içinde çalıştır; kurulum başına tam olarak bir zamanlayıcı bulunsun |
| `QUERYPROXY_WORKER_PROCESSES` | `1` | Yalnızca Docker: kuyruk işçisi süreç sayısı |
| `QUERYPROXY_WORKER_TIMEOUT` | çalıştırma zaman aşımı + 10 | İşçinin `--timeout` değeri; Docker entrypoint'i bunu `QUERYPROXY_EXECUTION_TIMEOUT`'tan türetir (ayrı bir işçi konteynerinde de aynıdır). Geçersiz kılınamaz: entrypoint her açılışta yeniden hesaplar ve verdiğiniz değeri yok sayar. Bunun yerine çalıştırma zaman aşımını değiştirin. Docker dışında `queue:work` için `--timeout`'u kendiniz ayarlayın |
| `QUERYPROXY_KEY_FILE` | `/var/www/html/storage/app/.app_key` | Yalnızca Docker: `APP_KEY` verilmediğinde üretilen anahtarın saklandığı dosya. Değişken ayarlı değilse ve eski yolda (`/var/www/html/database/.app_key`) bir anahtar dosyası zaten varsa, entrypoint o dosyayı kullanmaya devam eder |
| `QUERYPROXY_REQUIRE_2FA` | `none` | TOTP iki adımlı doğrulamayı zorunlu kıl: `none`, `admins`, `dba` (yöneticiler + DBA rolü olan herkes) veya `all` |
| `QUERYPROXY_LOCAL_LOGIN` | `all` | Parola girişinin kapsamı: `all`, `admins_only` (parolayı yalnızca sistem yöneticileri kullanır, break-glass erişimi için) ya da `none` (parola girişi ve parola sıfırlama kapalı; önce SSO çalışmalı). Bilinmeyen değer `admins_only` sayılır. Bkz. [Tek oturum açma](#tek-oturum-açma) |
| `QUERYPROXY_OIDC_ISSUER` / `QUERYPROXY_OIDC_CLIENT_ID` | — | İkisi de ayarlanınca OpenID Connect ile tek oturum açma açılır. Bkz. [Tek oturum açma](#tek-oturum-açma) |
| `QUERYPROXY_OIDC_CLIENT_SECRET` | — | İstemci secret'ı; `QUERYPROXY_OIDC_CLIENT_SECRET_FILE` onu dosyadan okur |
| `QUERYPROXY_OIDC_SCOPES` | `openid email profile` | Sağlayıcıdan istenen kapsamlar, boşlukla ayrılmış; bazı sağlayıcılar `groups` eklenmesini ister |
| `QUERYPROXY_OIDC_GROUP_CLAIM` | `groups` | Kullanıcının gruplarını listeleyen claim; `realm_access.roles` gibi noktalı yol izlenir |
| `QUERYPROXY_OIDC_ROLE_MAP` | — | Virgülle ayrılmış `grup:takım:rol` girdileri; `takım` slug ya da addır, `rol` `dba`, `developer` ya da `auditor`'dır. Her SSO girişinde uygulanır |
| `QUERYPROXY_SCIM_ENABLED` | `false` | `/scim/v2` altında SCIM 2.0 kullanıcı sağlamayı sunar; kapalıyken her SCIM yolu `404` döner. Bkz. [SCIM ile kullanıcı sağlama](/tr/docs/scim-kullanici-saglama/) |
| `QUERYPROXY_SCIM_RATE_LIMIT` | `300` | Her SCIM token'ı için dakikadaki istek; başarısız doğrulamalar adres başına aynı sayıyla sınırlanır |
| `QUERYPROXY_API_TOKEN_MAX_DAYS` | `90` | API kişisel erişim token'ının en uzun ömrü, gün olarak. Bkz. [REST API ve token'lar](/tr/docs/rest-api/) |
| `QUERYPROXY_API_RATE_LIMIT` | `60` | Her token için dakikadaki API çağrısı; başarısız kimlik doğrulamalar da adres başına aynı sayıyla sınırlanır |
| `QUERYPROXY_CHAT_WEBHOOK_ALLOWED_HOSTS` | — | Giden chat webhook'ları için ek izinli hostlar, virgülle ayrılmış ve `*` jokerli (SSRF koruması; `hooks.slack.com`, `*.webhook.office.com`, `*.logic.azure.com` ve `*.powerplatform.com` varsayılan olarak izinlidir) |
| `QUERYPROXY_CHAT_INCLUDE_SQL` | `literals_redacted` | Chat bildirimlerindeki SQL önizlemesi: `literals_redacted` (metin ve sayı literalleri `?` ile değiştirilir), `full` (gönderildiği gibi) veya `none`. Eski boolean değerler de çalışır: `true` `full`, `false` `none` demektir |
| `QUERYPROXY_SLACK_CLIENT_ID` / `QUERYPROXY_SLACK_CLIENT_SECRET` | — | OAuth kurulumu için Slack uygulamasının istemci kimlik bilgileri. Bkz. [Slack entegrasyonu](/tr/docs/slack-entegrasyonu/) |
| `QUERYPROXY_SLACK_SIGNING_SECRET` | — | Slack uygulamasının signing secret'ı; Slack'in gönderdiği her isteği doğrular |
| `QUERYPROXY_SLACK_RATE_LIMIT` | `300` | Slack çalışma alanı başına dakikadaki istek; imza doğrulandıktan sonra sayılır (öncesinde bir adres dakikada 120 ile sınırlıdır) |
| `QUERYPROXY_SECRET_CACHE_TTL` | `300` | Çözülmüş harici sırrın süreç belleğinde kalma süresi, saniye; `0` önbelleği kapatır. Bkz. [Harici gizli bilgi referansları](/tr/docs/harici-gizli-bilgiler/) |
| `QUERYPROXY_SECRET_TIMEOUT` | `5` | Bir sır deposunun bağlanması ve yanıtlaması için tanınan saniye |
| `QUERYPROXY_VAULT_*`, `QUERYPROXY_AWS_REGION`, `QUERYPROXY_AZURE_*` | — | HashiCorp Vault, AWS Secrets Manager ve Azure Key Vault bağlantısı; tüm değişkenler [Harici gizli bilgi referansları](/tr/docs/harici-gizli-bilgiler/#ayarlar) sayfasında |
| `QUERYPROXY_PENDING_REMIND_AFTER` | `4` | Bir talebin `pending` kalabileceği saat sayısı; sonrasında takımın DBA'lerine ve yöneticilerine portalda ve chat'te bir hatırlatma gider. `0` hatırlatmayı kapatır; talepler her iki durumda da zaman aşımına uğramaz |
| `QUERYPROXY_METRICS_TOKEN` | — | `GET /metrics` için bearer token; 24 ya da daha fazla rastgele karakter. Bu ya da izin listesi ayarlanana kadar `/metrics` `404` döner. Bkz. [İzleme](/tr/docs/izleme/) |
| `QUERYPROXY_METRICS_ALLOWED_IPS` | — | `/metrics`'i token olmadan okuyabilecek adreslerin ya da CIDR aralıklarının virgülle ayrılmış listesi (`TRUSTED_PROXIES` kurallarını izler) |
| `QUERYPROXY_SIEM_SINKS` | — | Açılacak denetim aktarım hedefleri: `stdout`, `syslog`, `webhook`. `QUERYPROXY_SIEM_*` ayarları [Denetim zinciri ve SIEM aktarımı](/tr/docs/denetim-kaydi-ve-siem/) sayfasında listelenir |
| `QUERYPROXY_CONNECTION_HOST_DENYLIST` | — | Bağlantı hedefi olarak engellenen ek hostlar (virgülle ayrılmış) |
| `QUERYPROXY_SQLITE_ALLOWED_DIR` | — | SQLite bağlantı dosyalarını bu dizinle sınırla. Uygulama dizini içindeki dosyalar (QueryProxy'nin varsayılan SQLite veritabanı orada durur) bu ayar verilsin ya da verilmesin her zaman reddedilir |
| `QUERYPROXY_EXTRA_UNTRUSTED_LANGUAGES` | — | Guard'ın `CREATE EXTENSION`'da reddettiği ek prosedürel diller (virgülle ayrılmış); yerleşik listeyi genişletir. `DO`, `CREATE FUNCTION` ve `CREATE PROCEDURE` bu ayardan bağımsız olarak yalnızca `plpgsql` ve `sql` kabul eder |
| `QUERYPROXY_EXTRA_DANGEROUS_VARIABLES` | — | Guard'ın `SET` ile (MySQL / MariaDB'de kalıcı scope'lar, PostgreSQL'de her scope) ve `set_config` ile yazılmasını reddettiği ek sunucu değişkenleri (virgülle ayrılmış); yerleşik listeyi genişletir |
| `QUERYPROXY_EXTRA_BLOCKED_FUNCTIONS` | — | Guard'ın doğrudan reddettiği ek fonksiyonlar (virgülle ayrılmış); yerleşik listeyi genişletir |
| `QUERYPROXY_EXTRA_STATE_CHANGING_FUNCTIONS` | — | Bir ifadeyi yazma yapan ek fonksiyonlar (virgülle ayrılmış); yerleşik listeyi genişletir |
| `QUERYPROXY_EXTRA_RESOURCE_CONSUMING_FUNCTIONS` | — | Kaynak tuttukları için (uyku/sleep gibi) bir ifadeyi yazma yapan ek fonksiyonlar (virgülle ayrılmış); yerleşik listeyi genişletir |
| `QUERYPROXY_EXTRA_BLOCKED_PROCEDURES` | — | Guard'ın reddettiği ek SQL Server prosedürleri (virgülle ayrılmış); yerleşik listeyi genişletir |
| `QUERYPROXY_EXTRA_SQLITE_PRAGMAS` | — | Çıplak, okuma biçiminde izin verilen ek SQLite pragma'ları (virgülle ayrılmış); yerleşik listeyi genişletir |
| `TRUSTED_PROXIES` | — | `X-Forwarded-*` başlıklarını ayarlamasına izin verilen ters vekillerin IP'leri / CIDR'ları (virgülle ayrılmış); boşsa hiçbir vekile güvenilmez |
| `TRUSTED_HOSTS` | — | `APP_URL`'deki host'a ek olarak bu kurulumun yanıt verdiği hostname'ler (virgülle ayrılmış) |
| `DB_QUEUE_RETRY_AFTER` | `QUERYPROXY_EXECUTION_TIMEOUT` + 30 (`330`) | Rezerve edilmiş bir kuyruk job'unun kaç saniye sonra yeniden deneneceği. Ayarlanmamışsa çalıştırma zaman aşımından türetilir; açıkça verilen değer önceliklidir. `BEANSTALKD_QUEUE_RETRY_AFTER` ve `REDIS_QUEUE_RETRY_AFTER` aynı şekilde çalışır |

> `retry_after` türetilir: `DB_QUEUE_RETRY_AFTER`'ı (ya da Beanstalkd / Redis
> karşılığını) kendiniz ayarlamadıkça `QUERYPROXY_EXECUTION_TIMEOUT` + 30
> saniyedir (varsayılan `300` için `330`). Docker entrypoint'i, işçinin
> `--timeout` (çalıştırma zaman aşımı + 10) ve `stopwaitsecs` (çalıştırma zaman
> aşımı + 20) değerlerini de aynı ayardan türetir. `retry_after`'ı açıkça
> ayarlarsanız çalıştırma zaman aşımının üzerinde tutun, çünkü sorgu job'u o
> noktada durdurulur; varsayılan +30 bir pay bırakır. Ona eşit ya da altındaki
> bir değer, yavaş bir sorgunun hâlâ çalışırken yeniden rezerve edilmesine ve
> sonra başarısız işaretlenmesine yol açar — yazması commit olabilse bile — ve
> `queue:work` (daemon ya da `--once`) veya `queue:listen` başlarken bir uyarı
> loglar. 0.2.0 `.env.example`'ından kalma sabit bir `*_QUEUE_RETRY_AFTER=330`
> türetmeyi durdurur; bilerek ayarlamadıysanız kaldırın.

Kabul edilen bir `QUERYPROXY_REQUIRE_2FA` TOTP kodu **yakılır**, dolayısıyla
penceresinin kalanında yeniden kullanılamaz. Pratik bir sonucu: aynı 30
saniyelik adımda ikinci bir cihazdan giriş yapmak için bir sonraki kodu beklemek
gerekir.

`QUERYPROXY_EXTRA_UNTRUSTED_LANGUAGES` ve `QUERYPROXY_EXTRA_DANGEROUS_VARIABLES`,
[SQL korumalarının](/tr/docs/sql-korumalari/) `CREATE EXTENSION` ve `SET`'i
değerlendirdiği iki listeye ekleme yapar. Yalnızca **genişletebilirler**:
yerleşik girdiler tabandır, ortamın verdiği her şeyin üstüne yeniden
eklenir; dolayısıyla kısaltılmış bir liste guard'ı etkisiz kılamaz.

### MySQL / MariaDB hedefleri: `sql_mode`

Guard tırnakları ve ters eğik çizgileri varsayılan biçimde okur; bu yüzden oturum
`sql_mode`'u bunları farklı okuyacaksa talep reddedilir. QueryProxy, bir `mysql`
ya da `mariadb` bağlantısında SQL'i çalıştırmadan önce `@@SESSION.sql_mode`
değerini okur; `NO_BACKSLASH_ESCAPES` ya da `ANSI_QUOTES` içeriyorsa talep
başarısız olur ve hiçbir şey çalışmaz; mesaj ilgili bayrağı adıyla belirtir.
QueryProxy'nin kendisi bağlanırken, sunucunun varsayılanının yerine geçen katı bir
`sql_mode` ayarlar; bu yüzden denetim tetiklenirse bağlandıktan sonra modu neyin
değiştirdiğine bakın (aradaki bir vekil, değişmiş bir bağlantı ayarı). Bayrakları
sunucu ve bağlantı kullanıcısı varsayılanlarından uzak tutmak yine de yerindedir,
ama tek başına sorunu gidermez. `sql_mode`'u değiştiren bir `SET` göndermek
her driver'da reddedilir
(bkz. [SQL korumaları](/tr/docs/sql-korumalari/)).

## Okuma kimlik bilgileri ve zaman aşımları

Bir okuma talebi, hedef veritabanında salt okunur bir oturumda; QueryProxy'nin
belirlediği bir ifade zaman aşımı ve kilit zaman aşımıyla çalışır. Driver
başına bunun anlamı:

| Driver | Salt okunur oturum | İfade zaman aşımı | Kilit zaman aşımı |
| :-- | :-- | :-- | :-- |
| PostgreSQL | `SET TRANSACTION READ ONLY` | `statement_timeout` (ve `idle_in_transaction_session_timeout`) | `lock_timeout` |
| MySQL | `SET SESSION TRANSACTION READ ONLY` | `MAX_EXECUTION_TIME`, **yalnızca SELECT** | `innodb_lock_wait_timeout` |
| MariaDB | `SET SESSION TRANSACTION READ ONLY` | `max_statement_time` | `innodb_lock_wait_timeout` |
| SQL Server | **yok** (motorda salt okunur oturum bulunmaz) | PDO sorgu zaman aşımı | `SET LOCK_TIMEOUT` |
| SQLite | `PRAGMA query_only` | yok | busy timeout |

İfade zaman aşımı `QUERYPROXY_EXECUTION_TIMEOUT` eksi 5 saniyedir (en az 1);
kilit zaman aşımı ise `QUERYPROXY_LOCK_TIMEOUT`'tur. Yazmalar da aynı sınırları
alır. MySQL'de ifade zaman aşımı yazmalara uygulanmaz; bu yüzden orada uzun bir
yazma veritabanı tarafından kesilmez, job çalıştırma zaman aşımında biter ve
yazma sunucuda yine de sürebilir. **Stop query** onu daha erken iptal eder.

MySQL ve MariaDB salt okunur modu tüm oturum için ayarlar; bu yüzden örtük
commit yapan bir ifade bu moddan sıyrılamaz. PostgreSQL'de bir okuma talebi
içinde `SELECT … FOR UPDATE` ve `FOR SHARE` reddedilir.

Bir bağlantı isteğe bağlı bir **okuma kullanıcı adı ve parolası** taşıyabilir
(ana kimlik bilgileri gibi şifrelenir). Okumalar bunları kullanır; yazmalar asla
kullanmaz. Bağlantı testi ikisini de dener ve okuma kimlik bilgilerinin hatası
"Read credentials:" ile başlar. Okuma kullanıcısına yalnızca `SELECT` yetkisi
verin. **SQL Server'da okumalar için veritabanı tarafındaki tek koruma budur**;
çünkü motorda salt okunur oturum yoktur ve `ApplicationIntent=ReadOnly` yazmalara
karşı koruma sağlamaz. SQLite bağlantıları bir okuma kimliği saklamaz.

## Hedef veritabanlarına TLS

Her ağ bağlantısının bağlantı formunda bir TLS ayarı vardır. Yeni bağlantılar
varsayılan olarak TLS'yi zorunlu tutar: düz metne düşmek yerine başarısız olurlar.

| Driver | Seçenekler | Yeni bağlantı |
| :-- | :-- | :-- |
| PostgreSQL | `require`, `verify-ca`, `verify-full`, `prefer`, `disable` | `require` (şifreli, sunucu doğrulanmaz). `verify-ca` ve `verify-full` yapıştırdığınız CA'yı kullanır |
| MySQL, MariaDB | `required` ya da driver varsayılanına bırakmak; "Verify the server certificate" | `required`, sunucu sertifikası doğrulanmaz |
| SQL Server | `required` ya da driver varsayılanına bırakmak; "Trust the server certificate" | `Encrypt=true`, `TrustServerCertificate=false` |
| SQLite | yok | yok |

PostgreSQL, MySQL ve MariaDB için bir CA sertifikasını, bir istemci sertifikasını
ve bir istemci anahtarını PEM olarak yapıştırabilirsiniz. Bunlar şifreli saklanır,
çalışma anında `storage/app/tls/<connection id>/` altına yazılır (dizin modu 0700,
dosyalar 0600) ve bağlantıyla birlikte silinir. Form bunları tarayıcıya asla geri
göndermez ve istemci anahtarı parola korumalı olmamalıdır. SQL Server formunda PEM
alanı yoktur.

Doğrulamasız `require` ve `required` trafiği şifreler ama karşı tarafın kim
olduğunu kanıtlamaz; dolayısıyla ortadaki adama (man in the middle) karşı koruma
sağlamaz. Ağ tümüyle size ait değilse doğrulamayı açın.

v0.3.0'dan önce var olan bağlantılar eski davranışını korur ve listede "TLS not
enforced" (PostgreSQL, `sslmode=prefer`) ya da "TLS: driver default" olarak
işaretlenir; `queryproxy:doctor` bunları adıyla bildirir. SQL Server'da zorunlu
TLS için `pdo_sqlsrv` eklentisi gerekir: o olmadan Laravel, şifreleme ayarını yok
sayan bir bağlantıya sessizce düşerdi; bu yüzden QueryProxy bağlanmayı reddeder.
`Encrypt=true` ile şifreleme sunmayan bir sunucuyu reddetmek ODBC sürücüsünün
belgelenmiş davranışıdır; QueryProxy'nin testleri böyle bir sunucuyu
simüle edemez.

## Dosyadan sırlar

Docker ve Kubernetes sırları dosya olarak gelir. Aşağıdaki her değişken için aynı
adı `_FILE` sonekiyle ayarlayın; entrypoint (ve uygulama) değeri o dosyadan okur:

| Değişken | Dosya değişkeni |
| :-- | :-- |
| `APP_KEY` | `APP_KEY_FILE` |
| `APP_PREVIOUS_KEYS` | `APP_PREVIOUS_KEYS_FILE` |
| `DB_PASSWORD` | `DB_PASSWORD_FILE` |
| `MAIL_PASSWORD` | `MAIL_PASSWORD_FILE` |
| `REDIS_PASSWORD` | `REDIS_PASSWORD_FILE` |
| `SLACK_BOT_USER_OAUTH_TOKEN` | `SLACK_BOT_USER_OAUTH_TOKEN_FILE` |
| `QUERYPROXY_ADMIN_PASSWORD` | `QUERYPROXY_ADMIN_PASSWORD_FILE` |
| `QUERYPROXY_METRICS_TOKEN` | `QUERYPROXY_METRICS_TOKEN_FILE` |
| `QUERYPROXY_SIEM_WEBHOOK_SECRET` | `QUERYPROXY_SIEM_WEBHOOK_SECRET_FILE` |
| `QUERYPROXY_OIDC_CLIENT_SECRET` | `QUERYPROXY_OIDC_CLIENT_SECRET_FILE` |
| `QUERYPROXY_VAULT_TOKEN` | `QUERYPROXY_VAULT_TOKEN_FILE` |
| `QUERYPROXY_VAULT_SECRET_ID` | `QUERYPROXY_VAULT_SECRET_ID_FILE` |
| `QUERYPROXY_AZURE_CLIENT_SECRET` | `QUERYPROXY_AZURE_CLIENT_SECRET_FILE` |

```yaml
services:
  app:
    environment:
      APP_KEY_FILE: /run/secrets/queryproxy_app_key
      DB_PASSWORD_FILE: /run/secrets/queryproxy_db_password
      QUERYPROXY_METRICS_TOKEN_FILE: /run/secrets/queryproxy_metrics_token
    secrets: [queryproxy_app_key, queryproxy_db_password, queryproxy_metrics_token]
```

Kubernetes'te `_FILE` değişkenini bir `Secret` volume'ünün bağlandığı yola
yönlendirin.

- Hem `X` hem `X_FILE` ayarlamak bir hatadır ve konteyner başlamaz. `null`,
  `(null)` ya da `(empty)` olarak ayarlanmış düz bir değişken ayarsız sayılır.
- Dosya var olmalı, okunabilir olmalı ve boş olmamalıdır. Sondaki satır sonları
  kaldırılır; geri kalan her şey korunur.
- Hata mesajları değişkeni ve yolu adıyla belirtir, değeri asla yazmaz.
- `php artisan config:cache` (imaj her açılışta çalıştırır) çözümlenmiş değerleri,
  sırlar dahil, `bootstrap/cache/config.php` içine düz metin olarak yazar. Dolayısıyla
  dosyadan gelen bir sır da bu cache dosyasında bulunur.
- Slack ve Teams'in gelen webhook kipindeki imza sırları ile webhook URL'leri
  ortam değişkeni değildir; uygulama veritabanında şifreli saklanır. Slack
  uygulamasının kendi kimlik bilgileri (`QUERYPROXY_SLACK_CLIENT_SECRET`,
  `QUERYPROXY_SLACK_SIGNING_SECRET`) düz ortam değişkenleridir ve `_FILE` biçimi
  yoktur.

`php artisan queryproxy:doctor` hangi sırların dosyadan okunduğunu bildirir
(`secret_files`) ve biri okunamıyorsa başarısız olur. Bkz.
[Sorun giderme](/tr/docs/sorun-giderme/).

## Tek oturum açma

OpenID Connect ile tek oturum açma, yukarıdaki tablodaki `QUERYPROXY_OIDC_*`
değişkenleriyle yapılandırılır. Sağlayıcıda redirect URI olarak
`{APP_URL}/auth/oidc/callback` kaydedin. Sağlayıcı örnekleri, rol eşlemesi ve hesap
oluşturma ile bağlama kuralları [Tek oturum açma](/tr/docs/tek-oturum-acma/)
sayfasındadır. İstemci secret'ı, [Dosyadan sırlar](#dosyadan-sırlar) bölümündeki diğer sırlar gibi dosyadan
gelebilir (`QUERYPROXY_OIDC_CLIENT_SECRET_FILE`).

## Şema gezgini

Query Studio'nun şema paneli ve ad tamamlaması, hedef veritabanının kataloğunu
sabit sorgularla, salt okunur bir oturumda ve onaysız okur. Yalnızca bağlantıyı
kullanabilen kullanıcılar okuyabilir; yanıt tabloları, görünümleri ve sütunları
listeler, veriyi asla.

- `QUERYPROXY_SCHEMA_TIMEOUT` bu okumanın ifade zaman aşımıdır, saniye cinsinden.
  Taleplerin yürütme zaman aşımından ayrıdır. Okuma ayrıca boş bir bağlantı yuvası
  ister (`QUERYPROXY_CONNECTION_SLOTS`); bütün yuvalar doluyken panel kullanıcıdan
  kısa süre sonra yeniden denemesini ister.
- Sonuç bağlantı başına `QUERYPROXY_SCHEMA_CACHE_TTL` saniye önbellekte tutulur;
  bağlantıyı düzenlemek yeni bir kayıt başlatır. Elle **Refresh** önbelleği atlar
  ve kullanıcı ile bağlantı başına dakikada `QUERYPROXY_SCHEMA_REFRESH_LIMIT` ile
  sınırlıdır. Önbellekte yalnızca adlar ve tipler durur; maskeli sütunlardaki kilit
  her yanıtta güncel maskeleme kurallarından hesaplanır.
- `QUERYPROXY_SCHEMA_MAX_COLUMNS` tek bir okumayı sınırlar. Daha büyük şema,
  varsayılan şema önce listelenerek bir tablo sınırında kesilir ve panel kesildiğini
  söyler.
- MySQL ve MariaDB'de yalnızca bağlantının kendi veritabanı listelenir. Sistem
  şemaları (`information_schema`, `mysql`, `performance_schema`, `sys`) asla
  listelenmez.
- Veritabanından yapılan her okuma, nesne ve sütun sayılarıyla denetim günlüğüne
  `schema.read` olarak yazılır. Önbellekten sunulan okuma yazılmaz.

## Uygulama anahtarı ve HTTPS

`APP_KEY`, saklanan bağlantı kimlik bilgilerinizi ve chat sırlarınızı şifreler.
**Üretimde açıkça belirleyin ve sabit tutun** — yeniden üretilen bir anahtar
(ör. konteyner yeniden oluşturulunca) saklanan tüm kimlik bilgilerini okunamaz
hale getirir. `php artisan key:generate --show` ile üretin ve döndürürken eski
anahtarı `APP_PREVIOUS_KEYS`'e (virgülle ayrılmış) taşıyın ki mevcut şifreli
veri hâlâ çözülebilsin.

HTTPS'i bir ters vekil arkasından sunarken `SESSION_SECURE_COOKIE=true` ayarlayın.

## Ters vekil arkasında

> **Kırıcı değişiklik — QueryProxy'nin önünde bir vekil varsa `TRUSTED_PROXIES`'i
> ayarlayın.** Önceki sürümler her vekile güveniyordu. Bu sürümden itibaren
> hiçbir vekile varsayılan olarak güvenilmez ve siz kendinizinkini yazana kadar
> uygulama bağlanan tarafı istemci olarak okur.

`TRUSTED_PROXIES`, `X-Forwarded-*` başlıklarını ayarlamasına izin verilen IP'lerin
ya da CIDR'ların virgülle ayrılmış listesini alır:

```dotenv
TRUSTED_PROXIES=10.0.0.0/8
```

Bu liste vekilinizin adını içermedikçe `X-Forwarded-Proto` yok sayılır — HTTPS
algılanmaz, dolayısıyla HSTS başlığı gönderilmez — ve denetim kayıtlarına
istemcinin değil vekilin adresi düşer.

**Asla `*` yazmayın.** Her vekile güvenmek, herhangi bir istemcinin kendi
`X-Forwarded-For` başlığını göndermesine izin verir; bu, denetim izindeki
istemci IP'sini sahteler ve sınırsız giriş ile 2FA denemesi hakkı verir, çünkü
iki throttle da IP başına anahtarlanır.

`X-Forwarded-Host`, listede ne yazarsa yazsın hiç dikkate alınmaz: sahte bir host
başlığı parola sıfırlama e-postasındaki bağlantıyı yeniden yazıyordu. İkinci bir
katman olarak istek host'u `APP_URL`'deki host'a (ve `www.` kardeşine)
sabitlenir; böylece mutlak URL'ler — imzalı route'lar, parola sıfırlama
e-postaları — her zaman kendi kurulumunuzu gösterir. `APP_URL`'yi kullanıcıların
gerçekten eriştiği adrese ayarlayın.

Kurulum birden fazla ada yanıt veriyorsa diğerlerini `TRUSTED_HOSTS`'e yazın:

```dotenv
TRUSTED_HOSTS=queryproxy.internal,db.example.com
```

`X-Forwarded-Host` yok sayıldığına göre çok alan adlı bir dağıtımı çalıştırmanın
desteklenen yolu budur — listelemediğiniz bir hostname yine de bir parola
sıfırlama bağlantısını yeniden yazamaz. Adlar `APP_URL`'deki host'a (ve `www.`
kardeşine) eklenir, onun yerine geçmez. Hostname olarak okunamayan girdiler
sessizce atılır, böylece bir yazım hatası kurulumu çökertemez.

İki değer de uygulama yapılandırması üzerinden okunur, bu yüzden `.env`'e yazmak
yeterlidir. Yapılandırmayı cache'liyorsanız değiştirdikten sonra
`php artisan config:cache`'i yeniden çalıştırın; Docker imajı her açılışta
yeniden cache'ler.

## Uygulama veritabanı

QueryProxy'nin kendi verisi (kullanıcılar, takımlar, talepler, denetim günlüğü)
varsayılan olarak SQLite'ta yaşar — sıfır yapılandırma. MySQL, MariaDB, PostgreSQL ya da
SQL Server için standart Laravel değişkenlerini ayarlayın:

```dotenv
DB_CONNECTION=pgsql        # veya mysql, mariadb, sqlsrv
DB_HOST=db
DB_PORT=5432
DB_DATABASE=queryproxy
DB_USERNAME=queryproxy
DB_PASSWORD=degistir-beni
```

Bu, *uygulama* veritabanıdır — proxy'lediğiniz veritabanları arayüzde
**Connections** altında, takım bazında ve şifreli kimlik bilgileriyle tanımlanır.

**SQLite'tan başka bir veritabanına geçiş.** QueryProxy'yi durdurun ve
`php artisan queryproxy:migrate-appdb --to=<dsn>` çalıştırın, örneğin
`--to=pgsql://user:password@host:5432/queryproxy` (paroladaki özel karakterleri
URL kodlamasıyla yazın). Komut hedefte migration'ları çalıştırır, her tabloyu
yabancı anahtar sırasıyla kopyalar, her tablonun satır sayısını karşılaştırır ve
hedefte `audit:verify` çalıştırır. Denetim trigger'ları yerinde kalır; denetim
hash'leri ve zincirin başı değişmeden kopyalanır. Hedef boş olmalıdır (yalnızca
migrate edilmiş bir veritabanı olabilir). `--dry-run` her şeyi denetler, hiçbir şey
yazmaz; `--force` olmadan komut QueryProxy'nin durduğunu onaylamanızı ister. Kopya
tek bir transaction'dır; bu yüzden hata, yeniden deneyebileceğiniz migrate edilmiş
ve boş bir hedef bırakır. `cache` tabloları kopyalanmaz, hedef parola hiç
yazdırılmaz. Sonra `DB_CONNECTION` ve diğer `DB_*` ayarlarını değiştirip
QueryProxy'yi yeniden başlatın.

**Uygulama veritabanı olarak SQL Server.** `mssql` image varyantını kullanın:
varsayılan image'ta `pdo_sqlsrv` yoktur (bkz. [SQL Server hedefleri](#sql-server-hedefleri)).
Bir kurulumu oraya taşımak için
`php artisan queryproxy:migrate-appdb --to="sqlsrv://user:password@host:1433/queryproxy?trust_server_certificate=true"`
kullanın. Sorgu dizesindeki `trust_server_certificate` ve `encrypt` gibi seçenekler
yalnızca o komutun bağlantısına gider. Uygulamada bunlar için ayrı bir `DB_*`
değişkeni yoktur; bu yüzden taşıdıktan sonra aynı DSN'i, aynı seçeneklerle `DB_URL`
içinde verin. Aksi hâlde kendinden imzalı sertifikalı bir sunucuya `migrate-appdb`
ulaşır ama çalışan uygulama ulaşamaz. `DB_URL` parolayı da taşır; bu yüzden
paroladaki özel karakterleri URL kodlamasıyla yazın ve onu `DB_PASSWORD` kadar gizli
tutun. SQL Server, bir tabloya birden fazla cascade yolu veren yabancı anahtarları
reddeder ve şemada bunlardan birkaç tane vardır. Bu yüzden yalnızca SQL Server'da
sekiz yabancı anahtar `ON DELETE NO ACTION` ile oluşturulur: `query_requests.team_id`,
`masking_rules.team_id`, `connection_grant_requests.team_id`,
`connections.created_by`, `connection_user.granted_by`,
`connection_grant_requests.decided_by` ve kullanıcılara giden iki `query_requests`
anahtarı (`user_id` ve `reviewed_by`); bu ikisi, talep geçmişi bir kullanıcıyla
birlikte asla silinmesin diye her sürücüde `RESTRICT`'tir (ADR-018). Silme sırasında veritabanının yapacağını
QueryProxy yapar: bir takımı silmek önce talepleri, maskeleme kurallarını ve
tanımlama taleplerini siler; hiç talebi olmayan bir kullanıcıyı kalıcı silmek, ona
başvuran anahtarları `NULL` yapar.

Fark, bir takımı ya da kullanıcıyı silen **ham SQL**'de ortaya çıkar. SQL Server'da
bu silme, takıma ya da kullanıcıya bu sekiz anahtardan biriyle hâlâ başvuran satır
varken reddedilir: takım için talepler, maskeleme kuralları ya da tanımlama
talepleri; kullanıcı için oluşturduğu bağlantılar, verdiği tanımlamalar, karara
bağladığı tanımlama talepleri ya da açtığı veya incelediği talepler. Diğer sürücülerde
cascade yapan ya da `NULL` yapan altı anahtar için SQL Server'da silme, bunun yerine
reddedilir. Kullanıcılara giden iki talep anahtarı her sürücüde reddedilir.
PostgreSQL, MySQL, MariaDB ve SQLite'ta hiçbir şey değişmez: yeni migration yok,
mevcut kurulumda değişiklik yok, silme sonuçları aynı.

**Test edilen sürümler.** Hedef veritabanları: PostgreSQL 17, MySQL 8.4,
MariaDB 11.4 ve 11.8, SQL Server 2022 ve SQLite. Uygulama veritabanı: SQLite,
MySQL 8.4, MariaDB 11.4 ve 11.8, PostgreSQL 17 ve SQL Server 2022 (`mssql` image
varyantı). PostgreSQL uyumlu hedef veritabanları: CockroachDB v25.4 ve
YugabyteDB 2025.2; bilinen farkları, SQL korumasının CockroachDB'de neleri
kapsamadığı dahil, [PostgreSQL uyumlu veritabanları](/tr/docs/uyumluluk/)
sayfasında listelenir. Diğer sürümler çalışabilir ama test edilmez.

## Kuyruk

Kuyruk varsayılan olarak veritabanı tabanlıdır (Redis gerekmez). Redis'e
taşımak için:

```dotenv
QUEUE_CONNECTION=redis
REDIS_HOST=redis
```

`REDIS_QUEUE_RETRY_AFTER` isteğe bağlıdır: ayarlanmamışsa
`QUERYPROXY_EXECUTION_TIMEOUT` + 30 saniyeden türetilir (bkz. ayar tablosunun
altındaki not). Yalnızca bu değeri geçersiz kılmak için ayarlayın ve çalıştırma
zaman aşımının üzerinde tutun.

İşçi komutu aynı kalır. `--timeout` değerini çalıştırma zaman aşımının biraz
üzerinde tutun (çalıştırma zaman aşımı + 10 saniye; `QUERYPROXY_EXECUTION_TIMEOUT`
varsayılan olarak `300`'dür, dolayısıyla `310`):

```bash
php artisan queue:work --queue=queries,default --tries=1 --timeout=310
```

Manuel kurulumda bu sayı sizin için türetilmez: `QUERYPROXY_EXECUTION_TIMEOUT`'u
değiştirirseniz `--timeout`'u da ona göre değiştirin (Docker entrypoint'i bunu kendisi
türetir).

Aynı uyarı (`retry_after`'ın çalıştırma zaman aşımına eşit ya da altında olması)
yalnızca daemon `queue:work` için değil, `queue:listen` ya da
`queue:work --once` başlarken de bir kez loglanır. `queue:listen`'ın başlattığı
kısa ömürlü `queue:work --once` süreçleri bunu tekrarlamaz.

Deponun `docker-compose.yml` dosyasındaki ayrı `worker` örneği artık
`--timeout=310` değerini sabit yazmaz: imaj entrypoint'inin
`QUERYPROXY_EXECUTION_TIMEOUT` + 10 olarak türettiği
`QUERYPROXY_WORKER_TIMEOUT`'u geçirir. O servis için
`QUERYPROXY_EXECUTION_TIMEOUT`'u ayarlayın, işçi onu izler. Bu, imajın varsayılan
entrypoint'ini gerektirir; bu yüzden worker servisinde `entrypoint:`'i geçersiz
kılmayın.

## S3 üzerinde sonuç depolama

`QUERYPROXY_RESULT_DISK=s3` ve standart `AWS_*` değişkenlerini ayarlayın. Sonuç
dosyaları NDJSON olarak, akış hâlinde yazılır — bellek kullanımı sonuç boyutundan
bağımsız olarak sabit kalır.

Disk **özel** olmalıdır. Sonuç yolları tahmin edilebilirdir
(`results/{team_id}/{id}.ndjson`); bu yüzden herkese açık sunulan bir disk, indirme
yetkilendirmesini tümden atlayarak her takımın satırlarını doğrudan web kökünden
dağıtırdı. Bu nedenle `QUERYPROXY_RESULT_DISK` `public` diskini ya da
`visibility => public` ile yapılandırılmış herhangi bir diski gösteriyorsa
QueryProxy sonuç yazmayı reddeder ve talep bunun yerine açık bir mesajla
başarısız olur.

## SQL Server hedefleri

Varsayılan Docker imajı `pdo_pgsql`, `pdo_mysql` ve `pdo_sqlite` ile gelir.
Microsoft SQL Server hedefleri için **`mssql` imaj varyantını** kullanın:
`queryproxy/queryproxy:<sürüm>-mssql` ya da son kararlı sürüm için
`queryproxy/queryproxy:mssql`. Microsoft'un ODBC Driver 18'ini ve `pdo_sqlsrv`
eklentisini ekler; `linux/amd64` ve `linux/arm64` için derlenir, varsayılan imaj
gibi taranır, imzalanır ve attestation taşır. `:mssql` yalnızca kararlı
sürümlerle ilerler ve `latest` etiketini asla almaz. Microsoft'un sürücüsü
Microsoft'un kendi lisansı altındadır (<https://aka.ms/odbc18eula>); varyantı
kullanmak onu kabul etmek demektir.

Varyantı kendiniz derlemek için deponun Dockerfile'ına `--build-arg
INSTALL_SQLSRV=1` verin. Onaylı sorgular işçide çalıştığı için eklentilerin hem web
sürecini hem kuyruk işçisini çalıştıran PHP derlemesinde bulunması gerekir: bir
kurulumun her rolünde aynı varyantı çalıştırın.

### Stop query için yetkiler

**Stop query**, çalışan ifadeyi ikinci bir bağlantıdan iptal eder. PostgreSQL,
MySQL ve MariaDB'de bağlantı kullanıcısının sahip olduğunun ötesinde ek yetki
gerekmez (başkalarının oturumları için `pg_signal_backend` ya da
`CONNECTION_ADMIN` yeterlidir). SQL Server'da bağlantının login'i şunlara
ihtiyaç duyar:

- `VIEW SERVER STATE`: bu olmadan `sys.dm_exec_sessions` yalnızca çağıranın kendi
  oturumunu gösterir, çalışma bulunamaz ve talep sunucuya sinyal gönderilmeden
  `cancelled` olarak işaretlenir (bir uyarı görünür ve ifade zaman aşımında biter).
- `ALTER ANY CONNECTION`: `KILL <spid>` buna ihtiyaç duyar.

SQL Server'da Stop **oturumu** sonlandırır; bu yüzden açık bir transaction
sunucuda geri alınır. Diğer driver'larda yalnızca ifade iptal edilir. Düğme
SQLite için gösterilmez.

Bir ifade başladıktan sonra QueryProxy ağ kopmasında yeniden bağlanmaz; çalışma,
yeni bir oturumda devam etmek yerine başarısız olur.
