Anahtarı elinde tutan bir ajan, arkasındaki her şeyi harcayabilir; sunucunuzda duran bir limit ise, bir şey onun etrafından dolaşana kadar limittir. Aynı limitin Stellar defterinde uygulanmış hali ve reddettiği anların testnet işlemleri.
Ajana anahtar vermenin problemi
Bir yazılım para hareket ettirebilen bir anahtarı eline aldığı anda, her kötü prompt, her hata ve her ele geçirilme bir ödemedir. Bu varsayımsal bir hata biçimi değil, varsayılan olanıdır.
Alışılmış cevap uygulamanın içine bir kontrol koymaktır: göndermeden önce sunucuya bu ödemenin serbest olup olmadığını sor. Bu, bir şey onun etrafından dolaşana kadar işe yarar. Ele geçirilmiş bir ajan, ikinci bir kod yolu, token'a doğrudan bir çağrı ve limitin aslında hiç limit olmadığı ortaya çıkar. Güvendiğiniz bir makinede çalışan bir öneriydi.
Stellar'da bir ajanın ödeme yapmak için ihtiyaç duyduğu parçalar zaten var: yerel USDC, çağrı başına fiyatlandırmayı saçma olmaktan çıkaracak kadar küçük ücretler ve hesapsız, kartsız API satın almak için x402. Olmayan şey, bu ajanın ne kadar harcayabileceğini sunucunun değil defterin sınırlamasıydı.
Kasa, tek paragrafta
Stellar testnet'e, ajanın USDC'sini tutan bir Soroban kontratı kurduk. Para artık ajanda değil. Ajanda olan şey, kasadan ödeme yapmasını isteme izni.
Kasa iki tarafı tanıyor. Kuralları koyan, her şeyi dondurabilen ve parayı çekebilen insan bir owner; yalnızca pay çağırabilen ve bunu yalnızca kuralların içinde yapabilen bir agent operator. Kontrat tek bir adresin ikisi birden olmasını reddediyor, yani çalınan tek bir anahtar hem politikanın ötesinde harcayıp hem politikayı yeniden yazamıyor.
Her ödemede dört kural çalışıyor: günlük bir tavan, tek bir ödeme için bir tavan, kime ödeme yapılabileceğinin listesi ve bir dondurma anahtarı. Bunlardan birini çiğneyen ödeme incelemeye takılmıyor. Hiç gerçekleşmiyor.
Neden defter, neden sunucu değil
Bu ayrım kulağa akademik geliyor ama değil. Sunucunuzdaki bir kontrolü, o sunucuyu kim çalıştırıyorsa o uygular. Kontrattaki bir kontrolü ağ uygular, ajanı yazan kişiye karşı da dahil.
Somut olarak, kasa kendi bakiyesini hareket ettiriyor. Ajanın sessizce yükseltebileceği bir allowance, kapıyı atlayan bir yönetici ucu ve sonradan böyle bir şey eklemeye yarayacak bir yükseltme girişi yok. Kontrat bilerek değiştirilemez kuruldu: yayına çıktığı kurallar, sahip olacağı tek kurallar.
Bunun bedeli gerçek ve söylenmeye değer. İçindeki bir hata yamalanamaz, ancak owner'ın parayı taşıyacağı yeni bir kasayla değiştirilebilir. Bu takası kabul ettik, çünkü yükseltme düğmesi olan bir korkuluk, arka kapısı olan bir korkuluktur.
Testnet'te gerçekte ne oldu
Kasaya 15 USDC kondu, günlük tavan 10 USDC ve tek ödeme tavanı 2 USDC olarak ayarlandı.
Ajanın 1 USDC'lik ödemesi gerçekleşti ve defterde duruyor, 3da74634 numaralı işlem. Günlük tavanı aşan bir ödeme ise tipli bir hatayla reddedildi: Error(Contract, #5), yani kasanın DailyCapExceeded demesi, 12df418f numaralı işlem.
Ardından owner kasayı dondurdu, kasa hala donmuşken owner yolundan allowlist'te olmayan bir adrese ödeme yaptı ve sonra dondurmayı kaldırdı. Üçü de defterde. Bu geçersiz kılma bir açık değil, kasıtlı: insan yolunun tam olarak ajan yolunun çalışmadığı anda çalışması gerekiyor. Yine de günlük tavandan düştü, yani owner kapıları atlıyor, bütçeyi değil.
Retler hakkında dürüst kısım
Retlerin çoğunun hiç işlem hash'i yok ve bunu, biri kanıttaki bir boşluk sanmadan önce açıklamak gerekiyor.
Soroban'da bir çağrı gönderilmeden önce simüle edilir. Kasa hayır dediğinde cevap simülasyon sırasında döner ve ağa hiçbir şey gönderilmez. Reddedilen bir ödeme normalde hiç iz bırakmaz; bu iyi mühendislik ve zahmetli bir kanıt durumu.
Bir denetçinin gerçekten açabileceği tek bir ret üretmek için, bir ödeme yoldayken limit sıkıldı ve ödeme simülasyonda değil uygulama anında düştü. Bu demo için bir numara değil. Bir insan, ajan alışverişin ortasındayken limiti düşürdüğünde gerçek bir ajanın karşılaştığı yarışın ta kendisi.
Ödeme rayı x402 ile nasıl buluşuyor
x402, bir ajanın abonelik olmadan API çağrısı satın aldığı kısım. Kaynağı istiyor, karşılığında fiyat ve adres taşıyan bir HTTP 402 alıyor, ödüyor ve ödemeyi ekleyerek tekrar soruyor.
İkisini birbirine bağlamak, satın alma parasının ajanın kendi cüzdanından değil kasadan çıkması demek. Satıcının allowlist'te olması, fiyatın tavanın altında kalması ve günün yerinin kalmış olması gerekiyor. Bunların hepsi, ortada bir işlem oluşmadan önce çalışıyor.
Böyle bir satın alma defterde duruyor, fab5c864 numaralı işlem. Aynı çağrı allowlist'te olmayan bir satıcıya yöneltildiğinde Error(Contract, #3), PayeeNotAllowed ile reddedildi ve bir önceki bölümdeki sebeple hiç işlem üretmedi.
Alıcı ayrıca ağ ücreti de ödemedi. Horizon, 22.973 stroop'luk ücreti alıcının değil bizim operator hesabımızın üzerine yazıyor; gassız derken kastedilen şey, bizim sözümüze güvenmek yerine bakıp doğrulayabildiğiniz bu.
İddia etmediklerimiz
Yalnızca testnet. Stellar mainnet'te ne kontratımız ne hesabımız var ve proof sayfamız bunu, biri yanlış okusun diye sessizlikte bırakmak yerine açıkça yazıyor.
Denetlenmedi. 52 birim testi var, ayrıca her güvenlik kontrolünü sırayla silip test paketinin kırmızıya dönmesini zorunlu kılan bir koşucu. İkinci kısım sayıdan daha ağır basıyor: yeşil bir paket kontratın testlerini geçtiğini kanıtlar, testlerin kontrat güvenli olmaktan çıktığında bunu fark edeceğini ise yalnızca o silme koşucusu kanıtlar. Yine de bu bir denetim değil ve öyle demeyeceğiz.
Stellar'da henüz ajan kimliği yok. Ajanın pasaportu bir EVM zincirinde duruyor ve buradan referansla gösteriliyor, çünkü onu tutturacak bir Soroban kaydı yok. Harcama korkuluğu kimlik katmanından önce yayına girdi; beklediğimizin tam tersi bir sıra ve sorun değil: defterin uyguladığı bir tavan, kimsenin kefil olmadığı bir ajana karşı da işe yarar.
Nereye bakmalı
Burada anılan her işlem testnet üzerinde stellar.expert'te duruyor; tam liste ve her birinin neyi kanıtlaması gerektiği Stellar proof sayfamızda. O sayfa, daha önce kaydettiğimiz bir cevabı tekrar oynatmak yerine, siz açtığınızda zinciri yeniden okuyor ve kontratın hala orada olduğunu kontrol ediyor.
Kontratın kaynağı public repoda soroban/contracts/agent-spend-policy altında; testler ve kapıları silen koşucu da orada.