Ana içeriğe atla Navigasyona atla Alt bilgiye atla

Rediacc'ı PocketOS Olayına Karşı Test Ettim

PocketOS lost their production database to a Cursor agent in 9 seconds. I ran the same test, timed every step, and noted what held and what's yours to fix.

Özet. Geçen hafta bir yapay zeka ajanı, PocketOS’un üretim veritabanını 9 saniyede sildi. Ben de kendi altyapımı aynı şekilde yıkmayı denedim. Altı koruma tuttu; bir tane dürüst boşluk hâlâ duruyor.

  • 128 GB üretim çatallaması, baştan sona: 7,2 saniye. CoW reflink’in kendisi: 2,3 saniye.
  • Ajan grand (üretim) depolarına erişimden engellendi, kendi geçersiz kılma değişkenini ayarlamasından engellendi ve erişim yetkilendirildiğinde bir kernel sandbox’a (yetkisiz kullanıcı, ayrı mount namespace, kapsamlanmış Docker soketi) düşürüldü.
  • Rediacc’ın izole etmediği şey: deponuzdaki verilerin içindeki harici SaaS kimlik bilgileri. Çatallama bunları miras alır. (Güncelleme. Mayıs 2026: rdc repo secret artık kimlik bilgilerini depo imajının tamamen dışında tutmanıza olanak tanıyor. Tüm hikaye için yeni kapanış bölümüne bakın: “Güncelleme: boşluk artık kapatılabilir”.) Bu kısmı Rediaccfile yaşam döngüsü kancaları üzerinden ele almak geliştiricinin işidir.

Güncelleme. Mayıs 2026. Bu yazı yayımlandığından bu yana, Rediacc birinci sınıf depo başına gizli anahtarlar (rdc repo secret) yayımladı. Aşağıda anlatılan “dürüst boşluk”. Depoya gömülmüş harici SaaS kimlik bilgileri. Artık gizli anahtarları LUKS imajının tamamen dışında tutan ve böylece çatallamaların varsayılan olarak hiçbirini miras almasına izin vermeyen yerleşik bir yanıta sahip. Neyin yayımlandığını, neyin değiştiğini ve hangi artık riskin kaldığını görmek için yeni kapanış bölümüne atlayın: Güncelleme: boşluk artık kapatılabilir.

Geçen hafta sonu, Jer Crane 30 saatlik bir postmortem yayımladı. Anthropic’in Claude Opus 4.6’sını çalıştıran bir Cursor ajanı, Railway üzerindeki üretim veritabanını sildi. Silme tek bir GraphQL çağrısıydı. 9 saniye sürdü. Railway’in volume yedekleri de onunla birlikte gitti, çünkü Railway yedekleri aynı volume’ün içinde tutuyor.

Şirketi PocketOS, araç kiralama işletmelerinin günlük operasyonlarını yürütmek için kullandığı bir yazılım yapıyor. Bu işletmelerin bir kısmı PocketOS’ta beş yıldır. Cumartesi sabahı müşteriler araçlarını almaya geldiklerinde kiralama firmalarının kim olduklarına dair hiçbir kaydı kalmamıştı. Üç aylık rezervasyon yok olmuştu. Jer günü, Stripe ödeme geçmişlerinden ve e-posta onaylarından ne kurtarabilirse kurtarmaya çalışarak geçirdi.

Yazısını iki kez okudum. The Register, Tom’s Hardware ve Business Standard hepsi haberi aldı. Hacker News başlığı 874 yoruma ulaştı.

Ben Rediacc’ı inşa ediyorum. Platform tam olarak bu tür bir başarısızlığı zorlaştırmak için var. Ben de oturup testi yaptım.

Bu yazıda bulduklarım var. Sayılar gerçek. Hata mesajları CLI’dan birebir alıntı. Rediacc’ın hiç korumadığı tek yer de burada. Aksini iddia etmek insanları başını derde sokan şeydir.

Aslında ne eksikti

Jer’in zaman çizelgesini dikkatle inceleyin. Üst üste binen dört ayrı arıza var.

  1. Cursor’ın kullandığı Railway API token’ı, özel alan adı yönetimi için oluşturulmuştu. Aynı zamanda volumeDelete yetkisine sahipti. Railway’in CLI token’larında işlem bazında kapsam yok.

  2. Railway’in GraphQL API’si volumeDelete’i tek bir POST olarak kabul ediyor. Onay adımı yok.

  3. Railway’in “volume yedekleri” aynı volume’ün içinde yaşıyor. Volume gittiğinde yedekler de gidiyor.

  4. Cursor ajanı, staging’deki bir kimlik bilgisi uyumsuzluğunu çözmenin doğru yolunun bir volume’ü silmek olduğuna kendi başına karar verdi.

  5. arızayı bir an için ayrı tutun. Cursor’ın sistem kuralları, ajana açık bir kullanıcı isteği olmadan asla yıkıcı git komutları çalıştırmamasını söylüyordu. Silmenin ardından ajan ne yaptığını açıklaması istendi. Yazılı bir itiraf verdi. Bir veritabanı volume’ünü silmenin “mümkün olan en yıkıcı, geri döndürülemez eylem olduğunu: force push’tan çok daha kötü” olduğunu kabul etti ve çiğnediği her güvenlik kuralını listeledi.

Bir davranış kuralı prompt’ta tavsiyedir. Yaptırım değildir. 1, 2 ve 3 numaralı arızalar altyapı tasarım kararlarıdır. 4. arızayı bir hatadan kaybedilmiş bir şirkete dönüştüren şey budur.

Test düzeneği

Rediacc çalıştıran hostinger adında gerçek bir üretim makinem var. Üzerinde on üç depo var: bir mail sunucusu, kendi barındırdığım GitLab, bir gözlemlenebilirlik stack’i ve karşılaştırma testleri için kullandığımız 128 GB’lık bir StackOverflow demosu. Disk %87 dolu. Boş alan neredeyse sıfır. Hataların acıtacağı türden bir makine.

StackOverflow demosunu bilerek seçtim. Kutudaki en büyük depo bu. Konteynerleri ve kalıcı verileriyle gerçek bir uygulama gibi kurulmuş. Eğer onun çatallanması hızlı ve izoleyse, ondan daha küçük her şey için de hızlı ve izoledir.

Test için ajanım, Claude Opus çalıştıran Claude Code idi. Cursor’unkiyle aynı model ailesi. Jer’in ajanının sahip olduğu erişim deseniyle aynı türden. Kullandığım CLI ise bizim kendi rdc’miz.

Birinci deneme: üretim deposuna doğrudan SSH

Ajanın (bu durumda benim) deneyeceği ilk şey en doğalı buydu: üretim deposuna bir kabuk açıp etrafa bakmak.

$ rdc term connect -m hostinger -r demo-stackoverflow -c "ls -la"

CLI reddetti. Birebir:

“demo-stackoverflow” is a grand (production) repository. Agents cannot modify grand repositories directly.

Grand repositories contain production data. Use a fork instead. Forks are safe, isolated sandbox copies.

Bu bir sistem prompt’u değil. Bu CLI’nın kendisi, çağrı dizüstünden dışarı çıkmadan önce reddediyor. CLI bir ajan olduğumu gördü. Claude Code CLAUDECODE ortam değişkenini ayarlıyor. CLI ayrıca bu değişkeni gizlemeye çalışan ajanları yakalamak için /proc üzerinden süreç ağacında dolaşıyor. Sonra işlemi politika tablosuyla eşleştirdi. Sonra reddetti.

Ajan da herhangi bir ajanın deneyeceği şeyi yapıyor. Geçersiz kılma değişkenini kendisi ayarlamayı deniyor.

$ REDIACC_ALLOW_GRAND_REPO=demo-stackoverflow rdc term connect ...

Yine reddedildi:

“demo-stackoverflow” is a grand (production) repository. Agent-initiated overrides are not accepted.

Do not attempt to set REDIACC_ALLOW_GRAND_REPO. Only the user can authorize this before the agent starts.

Aynı /proc taraması iki iş yapar. Önce ajanı tespit eder. Sonra geçersiz kılmanın ajanın içinde mi yoksa üstünde mi ayarlandığını kontrol eder. Sınırın altında: reddedilir. Üstünde: izin verilir.

Bunu test ettim. Ajandan çıktım. Kendi kabuğumda export REDIACC_ALLOW_GRAND_REPO=demo-stackoverflow çalıştırdım. Claude Code’u yeniden başlattım. Bağlantı bu kez çalıştı. Depoya ayrıcalıksız rediacc sistem kullanıcısı (UID 7111) olarak düştüm. DOCKER_HOST, ana deponun kapsamlanmış Docker daemon soketini gösteriyordu.

Ayrıca demo-stackoverflow için geçersiz kılma aktifken farklı bir üretim deposuna, nextcloud’a, bağlanmayı denedim. Reddedildi. Geçersiz kılma depo başına çalışıyor, genel bir anahtar değil.

İkinci deneme: depoyu çatalla ve çatallama üzerinde çalış

Rediacc’ın sizi kullanmaya teşvik ettiği iş akışı bu.

$ time rdc repo fork --parent demo-stackoverflow -m hostinger --tag agent-test

Terminalimden kopyalanan çıktı:

Config loaded     (9ms)
Connected         (1.1s)
Renet provisioned (1.2s)
Machine verified  (464ms)
License activated (2.1s)
✔ CoW clone complete (2.3s)

Total: 7.2s

128 GB’lık bir çatallama 2,3 saniyede. Sebebi bir BTRFS reflink. Çatallama, bir taraf yazana kadar ana deponun bloklarını gösteren metadata’dan ibarettir. Hiçbir veri taşınmaz.

Ölçeklemeyi kontrol etmek için aynı çatallamayı 2 GB’lık bir depoda çalıştırdım:

✔ CoW clone complete (573ms)
Total: 6.3s

2 GB çatallamasının klon adımı 573 milisaniye sürdü. 128 GB çatallamasının klon adımı 2,3 saniye sürdü. Yani veri klonu doğrusal altı, kesin sabit değil. Ama toplam çatallama süresi neredeyse aynı (6,3 sn’ye karşı 7,2 sn) çünkü beklemenin çoğu veri değil, SSH ve lisans dansı. Kullanıcı perspektifinden bakıldığında, çatallama depo boyutu fark etmeksizin 6 ile 7 saniye arası bir bekleme.

Şimdi çatallama üzerinde çalışıyorum. Kendi GUID’i (22c57178-...) var. Kendi networkId’si (21760). Kendi mount yolu. Kendi Docker daemon soketi (/var/run/rediacc/docker-21760.sock). Ana deponun verisi reflink üzerinden paylaşılıyor. Ana deponun kendisi tüm süreç boyunca çalışmaya devam ediyor.

Çatallamaya term connect yaptığımda gördüğüm şey şu:

$ rdc term connect -m hostinger -r demo-stackoverflow:agent-test -c "id"
uid=7111(rediacc) gid=7111(rediacc) groups=7111(rediacc),988(docker)

SSH kullanıcım değil bu. Kendi kabuğu olmayan, UID 7111’li adanmış rediacc sistem kullanıcısı. Oturum kendi mount namespace’inde çalışıyor. /proc/self/ns/mnt kontrol ettim. Çatallama oturumunun içinden ana deponun mount yolunu listelemeye çalıştığımda ls, Permission denied döndü. Ajan yolu tahmin etse bile üretim verisine kelimenin tam anlamıyla ulaşamıyor.

Tasarımın en önemli kısmı budur. Ajan kernel düzeyinde bir sandbox alıyor. “Lütfen yapma” talimatı değil. Ajan çatallama içinde rm -rf / çalıştırmaya karar verirse, en kötü durum ana depodan 2,3 saniyelik bir yeniden çatallama.

Platform desteği üzerine bir not

Geçersiz kılma yalnızca Linux’ta çalışıyor. Meşruiyet kontrolü /proc/<pid>/environ dosyasına ihtiyaç duyuyor. Bu dosya çekirdeğin her sürecin nasıl başlatıldığına dair kaydı. macOS ve Windows’ta dengi yok. Geçersiz kılmanın ajan tarafından değil sizin tarafınızdan ayarlandığını doğrulayacak bir yol olmadığında, CLI kapalı durumda başarısız oluyor. Doğru ayarlanmış bir geçersiz kılma bile bu platformlarda reddediliyor.

Hata mesajı ne yapacağınızı söylüyor:

The REDIACC_ALLOW_GRAND_REPO override is not supported on darwin. … To use the override, run your agent on Linux (directly, WSL, Docker, or a VM).

Pratikte, macOS veya Windows’taki ajanların önce-çatalla iş akışından kaçış yolu yok. Bu kasıtlı.

Bu testte tutan korumalar

İçeri bir veya iki güvenlik özelliğini doğrulamayı bekleyerek girdim. Altı tane ile çıktım. Her birinin gösterebileceğim bir kodu ve alıntılayabileceğim bir hata mesajı var.

  1. Grand-repo bloğu. Ajanlar grand (üretim) depoları üzerinde doğrudan işlem yapamaz. Çatallamak zorundalar.
  2. Ajan tarafından ayarlanan geçersiz kılmanın reddi. Kullanıcının ayarlayabileceği geçersiz kılma ortam değişkeni, ajanın kendi ortamında görünüyorsa reddediliyor.
  3. Depo başına geçersiz kılma kapsamı. demo-stackoverflow için verilen yetki nextcloud için hiçbir şey ifade etmiyor. Kapsam bir liste, bayrak değil.
  4. Kernel sandbox. Geçerli bir geçersiz kılmayla bile, oturum rediacc UID’si olarak, kendi mount namespace’inde, DOCKER_HOST tek bir deponun daemon’una kapsamlanmış şekilde çalışıyor. Diğer depoları görmenin yolu yok.
  5. Çevrimiçi çatallama. Ana depo çatallama boyunca çalışmaya devam etti. Kesinti yok, geçiş yok.
  6. Doğrusal altı çatallama süresi. 128 GB için 2,3 saniye. 2 GB için 573 ms. Beklemenin çoğu SSH dansı, veri değil.

Rediacc’ın izole etmediği tek şey

Şimdi yazının daha zor kısmı.

Rediacc altyapıyı izole eder: diskteki dosyayı, Docker daemon’unu, mount namespace’ini, ağı. Deponuzun kimlik bilgilerini tuttuğu harici SaaS API’lerini izole etmez.

Bir çatallama, ana deponun bayt düzeyinde BTRFS reflink’idir. Ana deponun data/, .env veya secrets/ dizininde ne yaşıyorsa çatallamaya da gelir. Deponuz STRIPE_LIVE_KEY, AWS_ACCESS_KEY_ID veya bir Railway API token’ı içeriyorsa, çatallamadaki ajan bunları okuyabilir. Bu token’larla api.stripe.com’a, s3.amazonaws.com’a veya backboard.railway.app’e çağrı yapabilir. Dışarıdan bakıldığında bu çağrılar üretimden geliyormuş gibi görünür. Stripe veya AWS, çatallamayı ayırt edemez.

Bu, paylaşılan sorumluluk sınırı. Rediacc altyapı yarısını üstlenir. Harici servis yarısı uygulama kodunuzda yaşar.

Boşluğun geliştirici tarafını kapatmak size kalmış.

  • Üretim harici kimlik bilgilerini depoda hiç tutmayın. Bunları konteyner başlangıcında bir secrets manager’dan çekin. Çatallamanın konteynerleri tasarım gereği yalnızca sandbox kapsamındaki kimlik bilgilerini çeker.
  • Çatallama anında Rediaccfile’ın up() kancası üzerinden kimlik bilgilerini çıkarın veya değiştirin. Bir çatallamanın up()’ı, ana depodan farklı bir depo GUID’siyle çalışır. Bunu tespit edin. Sonra .env’i sandbox değerleriyle yeniden yazın.
  • Çatallama başına harici kaynaklar sağlayın: çatallama başına bir Stripe sandbox hesabı, çatallama başına bir test veritabanı, çatallama başına bir S3 kovası.

PocketOS Rediacc üzerinde olsaydı, Railway API token’ı doğru karşılaştırma olmazdı. Altyapıları Rediacc çatallamasının kendisi olurdu. Bulunacak bir Railway token’ı olmazdı, çünkü Rediacc kimlik doğrulamış bir ajana volumeDelete benzeri hiçbir şey sunmaz. Ajan, ana depoyu silmenin hiçbir yolu olmayan, çatallama başına bir Docker soketinin içinde yaşardı.

Ama onların ajanı bir kimlik bilgisi dosyasında Stripe üretim anahtarı bulsaydı, Rediacc ajanın gerçek müşteri kartlarına iadeler yapmasını engellemezdi. Bu gerçek bir kayıp. Her iki şey de doğru.

Güncelleme: boşluk artık kapatılabilir

Yukarıdaki bölümü ilk yazdığım haliyle bıraktım. O zamanki dürüstlüğün ağırlığı vardı. Yayımladığımdan bu yana eksik parçayı ekledik ve boşluk artık kapatılabilir. Çatallamaların davranışını değiştirerek değil, kimlik bilgilerini şifreli depo imajının dışında tutacak bir yer ekleyerek.

Mekanizma rdc repo secret. GitHub Actions secrets üzerine modellendi. Nasıl inşa edildiğine dair iki şey önemli.

Gizli anahtarlar LUKS imajının içinde yaşamaz. Diskte ayrı bir düzlemde yaşıyorlar: env modundaki gizli anahtarlar konteynerlere compose dosyanızdaki ${REDIACC_SECRET_<KEY>} interpolasyonu olarak ulaşır, file modundaki gizli anahtarlar ise Docker compose’un secrets: bloğu üzerinden /run/secrets/<key> yolundaki bir tmpfs dosyası olarak konteynerlere ulaşır. Her iki durumda da değer asla deponun şifreli imajına dokunmaz. Çatallamanın BTRFS reflink’i imajı kopyalar. İmajda hiç olmayan şeyi kopyalamaz. Yeni bir çatallamanın gizli anahtar listesi boştur. Konteynerleri üretim kimlik bilgisi olmadan başlar; üretim çağrılarını yapmak bile mümkün değildir.

Model yalnızca yazılabilir. rdc repo secret get değer yerine bir SHA-256 özetini döner. CLI üzerinden bir gizli anahtarı geri okumanın yolu yok. Tasarım gereği. Bu, terminal kayıtlarının, kabuk geçmişinin ve kazara yönlendirmelerin sızıntı yüzeyini kapatır. Rotasyon, önceki değeri doğrulamak için --current <önceki-değer> veya önceki değere sahip olmadığınızda ön koşulu atlamak için --rotate-secret (rotasyon olarak denetlenir). İnsanlar ve ajanlar için simetrik. Her iki kapı da eşit şekilde uygulanır.

PocketOS senaryosu için şu anlama geliyor: Rediacc üzerinde olsalardı ve gizli anahtarları benimsemiş olsalardı, çatallama içindeki ajan bir kimlik bilgisi dosyasında STRIPE_LIVE_KEY bulamazdı. Çatallamanın gizli anahtar haritası boş olurdu. Sandbox içinden Stripe’a çağrı yapmak için ortada bir şey kalmazdı. Yukarıdaki orijinal bölümün “gerçek kayıp” niteleyicisi “kimlik bilgilerini imaja gömen depolar için gerçek kayıp” haline gelir. Ve bu depolar için çözüm, yeni mekanizmaya tek seferlik bir geçiş.

Kalan dürüst boşluk şu: rdc repo secret’ı benimsemeden önce depo imajına yazdığınız gizli anahtarlar. Bir volume’a işlenmiş bir .env, bir veritabanına kalıcılaştırılmış bir kimlik bilgisi, içinde token bulunan bir YAML dosyası. Bunlar hâlâ imaj verisinin parçası. CoW reflink onları eskisi gibi taşır. Mekanizma, onları koymak için güvenli bir yer verir; şifreli volume içinde diskte zaten olan veriyi geriye dönük olarak kaldırmaz. Düzeltme bugün manuel bir taşıma (değeri okuyun, rdc repo secret ile ayarlayın, imaj kaynağından temizleyin). İlk sınıf bir rdc repo secret lift --from .env özelliği yol haritasında.

Ajan tehdit modeli açısından bu tasarımın iki yan etkisi özellikle dikkat çekici. Birincisi, repo secret list ve repo secret get MCP araçları olarak açığa çıkar (okumaya güvenli. Adlar ve özetler, asla değerler), böylece bir uygulama kurmanıza yardım eden bir ajan, ne olduğunu görmeden neyin yapılandırıldığını keşfedebilir. İkincisi, bir yazma işlemi ön koşulu sağlamadığında, JSON hata zarfı, ajanın insana aynen aktarması gereken somut komutları içeren yapılandırılmış bir next.options[].run alanı içerir. “Önceki değeri unuttum” durumu için rotasyon kaçış kapısı dahil. Tam model için Yapay Zeka Ajan Güvenliği sayfasına bakın.

Tam nasıl yapılır rehberi Depolar § Gizli Anahtarlar sayfasında. Üç adım. Önce değeri ayarlayın: rdc repo secret set --name <repo> --key <KEY> --value <val> --current "". Ardından compose dosyanızda ${REDIACC_SECRET_<KEY>} referansını kullanın. Son olarak, rdc repo fork komutunu çalıştırın ve fork’un boş bir gizli anahtar listesiyle başladığını doğrulayın.

Bu tür bir iş yapan biri için ne değişir

Bir yapay zeka ajanına, üretim ortamınızı silebilecek bir kimlik bilgisi ile kabuk erişimi verirseniz, soru “eninde sonunda yıkıcı bir şey yapacak mı” değildir. Soru ne zaman yapacağı ve ne kadar geri dönülebilir olacağıdır.

Rediacc’ta değişen şey: yıkıcı patlama yarıçapı bir çatallama ile sınırlandırılır. “Yanlış şeyi sil” hatasının maliyeti 2,3 saniyelik yeniden çatallama. Ajanın “düzeltmeye” karar verdiği bir kimlik bilgisi uyumsuzluğunun maliyeti aynı 2,3 saniyelik yeniden çatallama. Kernel sandbox, çoğu hatanın üretim verisine hiç ulaşmamasını sağlıyor.

Değişmeyen şey: deponuzda canlı harici kimlik bilgileri varsa, ajan onları kullanabilir. Bunu altyapı katmanında değil, uygulama katmanında düzeltmek size kalmış.

Rediacc’ın PocketOS olayının her parçasını engelleyeceğini iddia edecek değilim. PocketOS hikayesinin en kötü kısmı, gerçek bir yedek olmadan Railway veri silinmesiydi. Bu Rediacc’ta yaşanmazdı, çünkü hiçbir ajana uzanabileceği bir volumeDelete API’si vermiyoruz. Geriye kalan risk yüzeyi, yani bir ajanın kod tabanınızdaki kimlik bilgileriyle çağırabileceği SaaS API’leri, güvenlik hikayesinin sizin up() kancanızda yaşayan kısmı. Bizim izolasyon modelimizde değil.

Tam sayılar, birebir hata mesajları ve kontrol ettiğim kod yolları Yapay Zeka Ajan Güvenliği ve Korumalar sayfasında belgelendi. Kendi altyapınızda benzer bir test çalıştırmak isterseniz, çatallama iş akışı Depolar sayfasında. Yaklaşık 7 saniye sürüyor.