पहुँच नियंत्रण
adk-auth का उपयोग करके AI agents के लिए एंटरप्राइज़-स्तरीय पहुँच नियंत्रण।
अवलोकन
adk-auth भूमिका-आधारित पहुँच नियंत्रण (RBAC), स्कोप-आधारित प्राधिकरण, ऑडिट लॉगिंग और SSO समर्थन को ADK agents के लिए प्रदान करता है। यह इस बात पर सुरक्षित, सूक्ष्म-स्तरीय नियंत्रण सक्षम करता है कि कौन से उपयोगकर्ता किन tools तक पहुँच सकते हैं।
आर्किटेक्चर
┌─────────────────────────────────────────────────────────────────┐
│ Agent Request │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SSO Token Validation │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Google │ │ Azure AD │ │ OIDC Discovery │ │
│ │ Provider │ │ Provider │ │ (Okta, Auth0, etc) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ JWKS Cache │ ← Auto-refresh keys │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼ TokenClaims
┌─────────────────────────────────────────────────────────────────┐
│ Claims Mapper │
│ │
│ IdP Groups → adk-auth Roles │
│ ───────────────────────────────────────── │
│ "AdminGroup" → "admin" │
│ "DataAnalysts" → "analyst" │
│ (default) → "viewer" │
└─────────────────────────────────────────────────────────────────┘
│
▼ Roles
┌─────────────────────────────────────────────────────────────────┐
│ Access Control │
│ │
│ Role: admin │
│ ├── allow: AllTools │
│ └── allow: AllAgents │
│ │
│ Role: analyst │
│ ├── allow: Tool("search") │
│ ├── allow: Tool("summarize") │
│ └── deny: Tool("code_exec") ← Deny takes precedence │
└─────────────────────────────────────────────────────────────────┘
│
▼ Check Result
┌─────────────────────────────────────────────────────────────────┐
│ Audit Logging │
│ │
│ {"user":"alice","resource":"search","outcome":"allowed"} │
│ {"user":"bob","resource":"exec","outcome":"denied"} │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Tool Execution │
│ (only if access granted) │
└─────────────────────────────────────────────────────────────────┘
डिज़ाइन सिद्धांत
1. अस्वीकृति की प्राथमिकता
जब किसी भूमिका में अनुमति और अस्वीकृति, दोनों नियम हों, तो अस्वीकृति हमेशा प्रभावी होती है:
let role = Role::new("limited")
.allow(Permission::AllTools) // Allow everything...
.deny(Permission::Tool("admin")); // ...except admin
// Result: Can access any tool EXCEPT "admin"
2. बहु-भूमिका संयोजन
एकाधिक भूमिकाओं वाले उपयोगकर्ताओं को अनुमतियों का संघ प्राप्त होता है, लेकिन किसी भी भूमिका के अस्वीकृति नियम अब भी लागू होते हैं:
let ac = AccessControl::builder()
.role(reader) // allow: search
.role(writer) // allow: write
.assign("alice", "reader")
.assign("alice", "writer")
.build()?;
// Alice can access both "search" AND "write"
3. स्पष्टता, अंतर्निहितता पर प्राथमिक
अनुमतियाँ स्पष्ट होती हैं - डिफ़ॉल्ट रूप से कोई पहुँच प्रदान नहीं की जाती:
let role = Role::new("empty");
// This role grants NO permissions
ac.check("user", &Permission::Tool("anything")); // → Denied
4. प्रमाणीकरण और प्राधिकरण का पृथक्करण
- प्रमाणीकरण (SSO): यह सत्यापित करता है कि उपयोगकर्ता कौन है (JWT के माध्यम से)
- प्राधिकरण (RBAC): यह निर्धारित करता है कि वे किस तक पहुँच सकते हैं
// Authentication: validate JWT, extract claims
let claims = provider.validate(token).await?;
// Authorization: check specific permission
ac.check(&claims.sub, &Permission::Tool("search"))?;
// Combined: SsoAccessControl does both
sso.check_token(token, &permission).await?;
इंस्टॉलेशन
[dependencies]
adk-auth = "2.1.0"
# For SSO/OAuth support
adk-auth = { version = "2.1.0", features = ["sso"] }
मुख्य घटक
अनुमति
pub enum Permission {
Tool(String), // Specific tool by name
AllTools, // Wildcard: all tools
Agent(String), // Specific agent by name
AllAgents, // Wildcard: all agents
}
भूमिका
let analyst = Role::new("analyst")
.allow(Permission::Tool("search".into()))
.allow(Permission::Tool("summarize".into()))
.deny(Permission::Tool("code_exec".into()));
AccessControl
let ac = AccessControl::builder()
.role(admin)
.role(analyst)
.assign("alice@company.com", "admin")
.assign("bob@company.com", "analyst")
.build()?;
// Check permission
ac.check("bob@company.com", &Permission::Tool("search".into()))?;
ProtectedTool
स्वचालित अनुमति जाँच के साथ किसी tool को रैप करता है:
use adk_auth::ToolExt;
let protected = my_tool.with_access_control(Arc::new(ac));
// When executed, checks permission before running
protected.execute(ctx, args).await?;
AuthMiddleware
कई tools को एक साथ सुरक्षित करें:
let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);
ScopeGuard
JWT claims या session state से प्राप्त request-स्तरीय प्राधिकरण के लिए scopes का उपयोग करें:
use adk_auth::{ContextScopeResolver, ScopeGuard};
let guard = ScopeGuard::new(ContextScopeResolver);
let protected = guard.protect(my_tool);
RBAC + scopes का संयोजन
RBAC इस प्रश्न का उत्तर देता है, "क्या यह उपयोगकर्ता tool तक पहुँच सकता है?" Scopes इस प्रश्न का उत्तर देते हैं, "क्या यह विशिष्ट request अभी अधिकृत है?"
use std::sync::Arc;
use adk_auth::{AuthMiddleware, ContextScopeResolver, ScopeGuard};
let rbac = AuthMiddleware::new(ac);
let scoped = ScopeGuard::new(ContextScopeResolver);
let protected = scoped.protect(rbac.protect(transfer_tool));
SSO एकीकरण
समर्थित प्रदाता
| प्रदाता | निर्माता | जारीकर्ता |
|---|---|---|
GoogleProvider::new(client_id) | accounts.google.com | |
| Azure AD | AzureADProvider::new(tenant, client) या AzureADProvider::multi_tenant(client).with_allowed_tenants(["tenant-id"]) | login.microsoftonline.com |
| Okta | OktaProvider::new(domain, client) | {domain}/oauth2/default |
| Auth0 | Auth0Provider::new(domain, audience) | {domain}/ |
| Generic | OidcProvider::from_discovery(issuer, client) | कोई भी OIDC प्रदाता |
AzureADProvider::multi_tenant() कॉन्फ़िगर किए गए ऑडियंस के लिए किसी भी टेनेंट को स्वीकार करता है, जब तक कि आप इसे with_allowed_tenants(...) के साथ स्पष्ट रूप से प्रतिबंधित न करें।
TokenClaims
मान्य JWTs से निकाले गए दावे:
pub struct TokenClaims {
pub sub: String, // Subject (user ID)
pub email: Option<String>, // Email
pub name: Option<String>, // Display name
pub groups: Vec<String>, // IdP groups
pub roles: Vec<String>, // IdP roles
pub hd: Option<String>, // Google hosted domain
pub tid: Option<String>, // Azure tenant ID
// ... more standard OIDC claims
}
ClaimsMapper
IdP समूहों को adk-auth भूमिकाओं से मैप करता है:
let mapper = ClaimsMapper::builder()
.map_group("AdminGroup", "admin")
.map_group("Users", "viewer")
.default_role("guest")
.user_id_from_email()
.build();
user_id_from_email() केवल तभी ईमेल दावे का उपयोग करता है जब email_verified == true; अन्यथा यह sub पर वापस आ जाता है।
SsoAccessControl
SSO सत्यापन को RBAC के साथ एक ही कॉल में संयोजित करता है:
let sso = SsoAccessControl::builder()
.validator(GoogleProvider::new("client-id"))
.mapper(mapper)
.access_control(ac)
.audit_sink(audit)
.build()?;
// Validate token + check permission + audit log
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;
ऑडिट लॉगिंग
FileAuditSink
let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);
आउटपुट फ़ॉर्मेट (JSONL)
{"timestamp":"2025-01-01T10:30:00Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"search","outcome":"allowed"}
{"timestamp":"2025-01-01T10:30:01Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"code_exec","outcome":"denied"}
कस्टम ऑडिट सिंक
use adk_auth::{AuditSink, AuditEvent, AuthError};
use async_trait::async_trait;
pub struct DatabaseAuditSink { /* ... */ }
#[async_trait]
impl AuditSink for DatabaseAuditSink {
async fn log(&self, event: AuditEvent) -> Result<(), AuthError> {
// Insert into database
sqlx::query("INSERT INTO audit_log ...")
.bind(event.user)
.bind(event.resource)
.execute(&self.pool)
.await?;
Ok(())
}
}
उदाहरण
cargo check -p adk-auth
cargo check -p adk-auth --features sso
सुरक्षा की सर्वोत्तम प्रक्रियाएँ
| अभ्यास | विवरण |
|---|---|
| डिफ़ॉल्ट रूप से अस्वीकार करें | केवल स्पष्ट रूप से आवश्यक अनुमतियाँ प्रदान करें |
| स्पष्ट अस्वीकृतियाँ | खतरनाक कार्रवाइयों के लिए अस्वीकृति नियम जोड़ें |
| हर चीज़ का ऑडिट करें | अनुपालन के लिए लॉगिंग सक्षम करें |
| सर्वर-साइड पर सत्यापित करें | सर्वर पर हमेशा JWTs को सत्यापित करें |
| HTTPS का उपयोग करें | JWKS एंडपॉइंट के लिए सुरक्षित कनेक्शन आवश्यक हैं |
| कुंजियाँ रोटेट करें | JWKS कैश हर घंटे स्वचालित रूप से रीफ़्रेश होता है |
| टोकन का जीवनकाल सीमित करें | अल्पकालिक एक्सेस टोकन का उपयोग करें |
| Azure टेनेंट प्रतिबंधित करें | मल्टी-टेनेंट Azure ऐप्स के लिए, with_allowed_tenants(...) कॉन्फ़िगर करें |
| पहचान मैपिंग से पहले ईमेल सत्यापित करें | ईमेल असत्यापित होने पर user_id_from_email() अब sub पर वापस आ जाता है |
| निरस्तीकरण की योजना बनाएँ | टोकन निरस्तीकरण अंतर्निहित नहीं है; यदि आपको तुरंत पहुँच रोकने की आवश्यकता है, तो इसे कस्टम वैलिडेटर में लागू करें |
| महँगे स्कोप लुकअप को कैश करें | यदि आपका ScopeResolver बाहरी सिस्टम को कॉल करता है, तो प्रति अनुरोध/सत्र परिणाम को कैश करें |
प्रमाणीकरण ब्रिज
जब आप चाहते हैं कि adk-auth, adk-server के लिए पुन: उपयोग योग्य JWT-आधारित अनुरोध एक्सट्रैक्टर प्रदान करे, तब auth-bridge सक्षम करें:
use adk_auth::auth_bridge::JwtRequestContextExtractor;
use adk_auth::sso::{ClaimsMapper, GoogleProvider};
let extractor = JwtRequestContextExtractor::builder()
.validator(GoogleProvider::new("client-id"))
.mapper(ClaimsMapper::builder().user_id_from_email().build())
.build()?;
एक्सट्रैक्टर Bearer टोकन को मान्य करता है, user_id को ClaimsMapper के साथ मैप करता है, और JWT scope / scp क्लेम्स को RequestContext.scopes में अग्रेषित करता है।
सीक्रेट प्रदाता
टूल्स ToolContext::get_secret और
InvocationContext::get_secret के माध्यम से रनटाइम सीक्रेट्स तक पहुँचते हैं। इनके पीछे adk_auth::secrets::SecretProvider है,
जिसके क्लाउड कार्यान्वयन फीचर फ़्लैग्स के पीछे उपलब्ध हैं:
| प्रदाता | सुविधा |
|---|---|
| AWS Secrets Manager | aws-secrets |
| Azure Key Vault | azure-keyvault |
| GCP Secret Manager | gcp-secrets |
किसी रन से एक को इस रूप में लपेटकर संलग्न करें: SecretService
use adk_auth::secrets::{CachedSecretProvider, SecretProvider, SecretServiceAdapter};
use std::sync::Arc;
use std::time::Duration;
// Any SecretProvider — here wrapped in the cache
let cached = Arc::new(CachedSecretProvider::new(provider, Duration::from_secs(300)));
let service = Arc::new(SecretServiceAdapter::new(cached));
प्रति-टूल प्राधिकरण
डिफ़ॉल्ट रूप से, संदर्भ रखने वाला कोई भी टूल किसी भी सीक्रेट का नाम दे सकता है, और प्रदाता को केवल वही नाम दिखाई देता है — ऐसा कुछ भी अलग नहीं करता कि कोई मौसम टूल अपनी API कुंजी माँग रहा है या वही टूल भुगतान क्रेडेंशियल माँग रहा है। प्रदाता से संपर्क करने से पहले AuthorizingSecretService प्रत्येक टूल के लिए निर्णय लेता है:
use adk_auth::secrets::authorizing::{AuthorizingSecretService, SecretGrant};
use std::sync::Arc;
let service = Arc::new(
AuthorizingSecretService::new(inner)
.grant("weather_lookup", SecretGrant::none().name("weather-api-key"))
.grant("charge_card", SecretGrant::none().prefix("billing/"))
.with_audit_sink(audit_sink),
);
| नियम | व्यवहार |
|---|---|
| टूल के पास नाम को कवर करने वाली अनुमति है | अनुमति दी गई |
| टूल के पास ऐसी अनुमति है जो नाम को कवर नहीं करती | अस्वीकृत; प्रदाता को कभी कॉल नहीं किया जाता |
| उपकरण को कोई अनुमति नहीं | अस्वीकृत |
| अनुरोध में उपकरण की पहचान नहीं है | अस्वीकृत, जब तक grant_untooled इसे न खोले |
जब तक अनुमति न दी जाए, तब तक सब कुछ अस्वीकृत रहता है, और अस्वीकृति Unauthorized त्रुटि लौटाती है। अस्वीकृत नाम को कभी खोजा नहीं जाता, इसलिए वह प्रदाता-पक्ष के एक्सेस लॉग में पढ़ने के प्रयास के रूप में दिखाई नहीं देता।
पहचान ऐसी चीज़ नहीं है जिसे कोई टूल स्वयं घोषित करता है। LlmAgent भेजे गए टूल का नाम अनुरोध पर ऐप, उपयोगकर्ता, सत्र और इनवोकेशन के साथ अंकित करता है, इसलिए कोई टूल किसी अन्य टूल की पहचान प्रस्तुत नहीं कर सकता। कोई टूल केवल एक उद्देश्य जोड़ सकता है:
// inside a tool
let key = ctx.get_secret_for_purpose("weather-api-key", "call the forecast endpoint").await?;
नोट: टूल के रूप में इनवोक किया गया एजेंट एक
ToolContextको पार करता है, जिसकी अपनी कोई पहचान नहीं होती, इसलिए उस एजेंट के भीतर किए गए एक्सेस आंतरिक टूल की पहचान के बजाय बाहरी एजेंट की पहचान प्रस्तुत करते हैं। उसी के अनुसार अनुमति दें।
एक्सेस का ऑडिट
SecretAuditSink प्रत्येक निर्णय के लिए एक SecretAccessDecision प्राप्त करता है, जिसमें परिणाम, सीक्रेट का नाम, टूल, उपयोगकर्ता, इनवोकेशन और कारण शामिल होते हैं — लेकिन कभी भी सीक्रेट का मान नहीं। अनुमतियाँ भी info पर और अस्वीकृतियाँ warn पर लॉग की जाती हैं।
कैशिंग
CachedSecretProvider अपने TTL के लिए एक मान प्रदान करता है, फिर उसे दोबारा प्राप्त करता है। यह सीमित और रद्द करने योग्य है:
| नियंत्रण | व्यवहार |
|---|---|
with_max_entries(n) | अधिकतम n नाम कैश किए जाते हैं; पूरा होने पर सबसे कम हाल में उपयोग किया गया नाम हटा दिया जाता है। डिफ़ॉल्ट 128 है; 0 कैशिंग अक्षम करता है |
invalidate(name) | एक गुप्त मान तुरंत हटा देता है — किसी गुप्त मान के बदलने पर इसका उपयोग करें, ताकि उसके TTL के शेष समय के दौरान पुराना मान उपलब्ध न कराया जाए |
invalidate_all() | सब कुछ हटा देता है |
purge_expired() | समाप्त हो चुकी प्रविष्टियों को दोबारा पढ़े जाने की प्रतीक्षा किए बिना हटा देता है |
जब गुप्त नाम इनपुट से व्युत्पन्न किए जाते हैं, तब एक बाउंड महत्वपूर्ण होता है: इसके बिना कैश प्रक्रिया के पूरे जीवनकाल तक बढ़ सकता है।
कैश क्या करता है और क्या सुनिश्चित नहीं करता
एक TTL यह नियंत्रित करता है कि कैश क्या लौटाता है, न कि कोई मान प्रक्रिया की मेमोरी में कितनी देर तक रहता है। प्रविष्टियाँ समाप्त होने, बाहर निकाले जाने या अमान्य किए जाने पर शून्यीकृत कर दी जाती हैं, जिससे उनका निवास-समय लगभग TTL तक सीमित हो जाता है। यह कमी है, मिटाना नहीं — एक String को पहले ही पुनः आवंटित किया जा चुका हो सकता है, एलोकेटर द्वारा कॉपी किया गया हो सकता है, डिस्क पर स्वैप किया गया हो सकता है, या कोर डंप में कैप्चर किया गया हो सकता है। कैश के लिए डिबग आउटपुट को संवेदनशील जानकारी हटाकर दिखाया जाता है, ताकि डायग्नोस्टिक प्रिंट किसी मान को लीक न कर सके।
महत्वपूर्ण: एक साधारण
SecretProviderअपनी कोई नीति लागू नहीं करता — संदर्भ रखने वाला कोई भी टूल उन सभी नामों का अनुरोध कर सकता है जिन्हें आधारभूत क्रेडेंशियल पढ़ सकते हैं। प्रति-टूल सीमा प्राप्त करने के लिए इसेAuthorizingSecretServiceमें रैप करें, और क्लाउड क्रेडेंशियल को भी सीमित दायरे में रखें: प्रत्येक डिप्लॉयमेंट के लिए एक IAM पहचान, जिसे केवल उन सीक्रेट्स तक पहुँच हो जिनकी उस डिप्लॉयमेंट को आवश्यकता है। महत्वपूर्ण: प्रोवाइडर इंटरफ़ेस केवल सीक्रेट नाम लेता है। ADK स्तर पर प्रति-टूल अनुमति, नेमस्पेस या एक्सेस ऑडिट नहीं है, इसलिए संदर्भ रखने वाला कोई भी टूल उन सभी नामों का अनुरोध कर सकता है जिन्हें आधारभूत क्रेडेंशियल पढ़ सकते हैं। क्लाउड क्रेडेंशियल को स्वयं सीमित दायरे में रखें — प्रत्येक डिप्लॉयमेंट के लिए एक IAM पहचान, जिसे केवल उन सीक्रेट्स तक पहुँच हो जिनकी उस डिप्लॉयमेंट को आवश्यकता है — और प्रोवाइडर-पक्ष के ऑडिट लॉग को एक्सेस के रिकॉर्ड के रूप में मानें।
एक्सट्रैक्टर किससे सुरक्षा करता है
एक्सट्रैक्टर कॉन्फ़िगर करने से प्रत्येक गैर-सार्वजनिक रूट के लिए प्रमाणीकरण चालू हो जाता है:
| मार्ग | कॉन्फ़िगर किए गए एक्सट्रैक्टर के साथ व्यवहार |
|---|---|
/api/sessions/*, /api/apps/*, आर्टिफैक्ट, डीबग | मान्य टोकन के बिना 401 |
/api/ui/* — ब्रिज, सूचनाएँ, संसाधन | मान्य टोकन के बिना 401; प्रमाणित उपयोगकर्ता अनुरोध के मुख्य भाग में नामित किसी भी उपयोगकर्ता का स्थान लेता है |
/api/run* | वैध टोकन के बिना 401; प्रमाणित उपयोगकर्ता दिए गए उपयोगकर्ता को अधिलेखित करता है |
/health | सार्वजनिक |
UI ब्रिज स्थिति अनुरोध के बॉडी से लिए गए (app_name, user_id, session_id) पर आधारित होती है, इसलिए प्रमाणीकृत उपयोगकर्ता को बॉडी के मान के स्थान पर प्रतिस्थापित किया जाता है, उस पर भरोसा नहीं किया जाता। पंजीकृत UI संसाधन उसे पंजीकृत करने वाले उपयोगकर्ता को रिकॉर्ड करता है; केवल वही उपयोगकर्ता उसे पढ़ या प्रतिस्थापित कर सकता है, और किसी अन्य उपयोगकर्ता के संसाधन को पढ़ने पर 404 उत्तर मिलता है, ताकि URI का अस्तित्व प्रकट न हो।
कोई भी एक्सट्रैक्टर कॉन्फ़िगर न होने पर बाँधने के लिए कोई प्रमाणीकृत पहचान उपलब्ध नहीं होती, इसलिए रूट खुले रहते हैं और संसाधन वैश्विक रूप से दृश्यमान रहते हैं। प्रमाणीकरण वैकल्पिक है — ऐसे किसी भी परिनियोजन के लिए एक्सट्रैक्टर कॉन्फ़िगर करें जो एकल विश्वसनीय उपयोगकर्ता का न हो।
त्रुटि प्रबंधन
use adk_auth::{AccessDenied, AuthError};
use adk_auth::sso::TokenError;
// RBAC errors
match ac.check("user", &Permission::Tool("admin".into())) {
Ok(()) => { /* access granted */ }
Err(AccessDenied { user, permission }) => {
eprintln!("Denied: {} cannot access {}", user, permission);
}
}
// SSO errors
match provider.validate(token).await {
Ok(claims) => { /* token valid */ }
Err(TokenError::Expired) => { /* token expired */ }
Err(TokenError::InvalidSignature) => { /* signature invalid */ }
Err(TokenError::InvalidIssuer { expected, actual }) => { /* wrong issuer */ }
Err(e) => { /* other error */ }
}
पिछला: ← मूल्यांकन | अगला: टूल प्राधिकरण →
A2A एंडपॉइंट્સ
| मार्ग | प्रमाणीकरण |
|---|---|
GET /.well-known/agent.json | सार्वजनिक — सहकर्मी क्रेडेंशियल रखने से पहले कार्ड प्राप्त करते हैं |
POST /a2a | आवश्यक, जब कोई RequestContextExtractor कॉन्फ़िगर किया गया हो |
POST /a2a/stream | आवश्यक, जब RequestContextExtractor कॉन्फ़िगर किया गया हो |
JSON-RPC रूट एजेंट और टूल का कार्य निष्पादित करते हैं, इसलिए वे सेशन, आर्टिफैक्ट और डीबग राउटर के समान लेयर रखते हैं। कॉन्फ़िगर किया गया कोई एक्सट्रैक्टर न होने पर मांगने के लिए कोई क्रेडेंशियल नहीं होता और रूट खुले रहते हैं, इसलिए गेट जोड़ने से मौजूदा डिप्लॉयमेंट बाधित नहीं होता।
महत्वपूर्ण: ये रूट पहले राउटर रूट पर,
/apiपर लागू लेयर के बाहर, मर्ज किए गए थे। ऐसा डिप्लॉयमेंट जो हर दूसरे म्यूटेशन सरफ़ेस को प्रमाणित करता था, फिर भी उस पोर्ट तक पहुँच रखने वाले किसी भी क्लाइंट को एजेंट चलाने और उसकी लागत वहन करने देता था। यहcreate_app_with_a2aऔरServerBuilder::buildदोनों पर लागू था।
A2aServer::builder() डिफ़ॉल्ट रूप से 127.0.0.1:8080 से बाइंड होता है। इसे एक्सपोज़ करने के लिए bind_addr कॉल करें और ऐसा करने से पहले एक एक्सट्रैक्टर कॉन्फ़िगर करें। जनरेट किया गया a2a-server स्कैफ़ोल्ड भी इसी नियम का पालन करता है और व्यापक बाइंड का विकल्प चुनने के लिए BIND_HOST पढ़ता है।