PDLSS Permission Boundaries
Purpose, Duration, Limits, Scope, and Self-Instantiation — the five permission dimensions every registered agent declares.
Every agent registered on AstraSync has PDLSS boundaries that define what it can do, when, how much, where, and whether it can spawn sub-agents. Counterparties use these boundaries to make informed access decisions.
PDLSS is immutable per agent version. Once an agent is registered, its pdlss, model,
framework, agentType, and apiEndpoint are fixed for the life of that version. Attempting to
PATCH them returns 409 pdlss_immutable. To change permissions, use the Upgrade flow
(POST /api/agents/:id/upgrade): it mints a successor version carrying the new boundary, the old
id is retired and stops verifying, the successor carries the predecessor's trust score, and the
lineage between versions stays visible to the owner. In multi-admin organisations the successor
waits for a second admin's approval before it goes live. Ownership transfer is a separate feature
for changing an agent's owner, not its permissions. See Agent Access for the
registration and upgrade walkthrough.
The five dimensions
P — Purpose
Action categories the agent is allowed to perform.
{
categories: ['read_data', 'write_data', 'execute_action'],
allowedActions: ['search', 'create_report'],
deniedActions: ['delete_account']
}D — Duration
Time windows and session limits.
{
maxSessionDuration: 3600, // 1 hour max
ttl: 1800, // 30 min token lifetime
allowedDays: [1, 2, 3, 4, 5], // Weekdays only
allowedHours: { start: 9, end: 17 },
timezone: 'America/New_York'
}L — Limits
Two per-transaction limits: the Autonomous Limit (autonomousThreshold) and the Hard Limit
(approvalThreshold).
{
autonomousThreshold: 100, // up to $100: approved autonomously
approvalThreshold: 1000, // $1000 or more: denied
// between the two: held for the owner's approval
currency: 'USD'
}| Transaction value | Outcome |
|---|---|
| At or below the Autonomous Limit | Approved autonomously |
| Between the two limits | Held for the owner's passkey approval |
| At or above the Hard Limit | Denied |
Set only an Autonomous Limit and everything above it is denied (no approval band). Set only a Hard Limit and every spend below it asks the owner. Declare neither and any positive transaction is denied. There is no third "step-up" tier.
S — Scope
Resources, jurisdictions, and counterparty rules.
{
jurisdictions: ['US', 'AU'],
resources: ['/api/data/*'],
unverifiedCounterpartyPolicy: 'deny',
minCounterpartyTrustScore: 60
}S — Self-Instantiation
Rules for sub-agent spawning by an already-registered orchestrator. This is not the primary agent's own onboarding — primary agents always need human-in-the-loop or crypto-keypair step-up to register (see Agent Access). Self-instantiation governs whether a registered agent can mint downstream workers inside its own PDLSS envelope.
{
allowed: true,
maxSubAgents: 3,
maxDepth: 2,
inheritPermissions: true,
requireApproval: true
}"Ask me": owner approval on any dimension
Besides allowing or denying, the owner can ask to approve certain requests with their passkey. Every dimension supports it:
| Dimension | Field | Asks when |
|---|---|---|
| Purpose | purpose.approvalActions | the request uses one of these (permitted) actions |
| Duration | duration.outsideWindow: 'approve' | the request falls outside allowedDays / allowedHours |
| Limits | limits.autonomousThreshold + limits.approvalThreshold | the value is between the two limits |
| Scope | scope.approvalResources, scope.approvalJurisdictions, scope.approvalCounterparties | the request touches one of these |
| Self-instantiation | selfInstantiation.requireApproval: true | the agent asks to create a sub-agent |
startTime / endTime are always hard: outside them the request is denied, never asked.
When a rule asks, verify-access returns recommendation: "step_up_required" with a
stepUpApproval object and a failures[] entry naming the dimension. This is not a deny. Hold
the request and poll the approval; once the owner approves, the same request (same attempt id)
passes once. See verify-access response for the codes.
- Deny beats ask. If any dimension denies the request, it is denied; an approval never rescues a deny.
- One approval, one rule. Approving one rule never covers another.
- Dimension only. Failures name the dimension that asked, never the rule's detail (the agent never learns your lists or limits).
- Approvals expire. The owner has 30 minutes to decide by default.
- Organisation ceilings can ask too. An enterprise boundary can mark actions, resources, jurisdictions, counterparties, out-of-window requests or sub-agents as "asks first"; it applies to every agent in the organisation, including ones registered before it.
Complete Example: Customer Support Agent
A real-world PDLSS configuration combining all five dimensions:
{
purpose: {
categories: ['read_data', 'write_data'],
allowedActions: ['search', 'create_ticket', 'update_ticket'],
deniedActions: ['delete_account', 'export_all_data']
},
duration: {
maxSessionDuration: 3600,
ttl: 1800,
allowedDays: [1, 2, 3, 4, 5],
allowedHours: { start: 9, end: 17 },
timezone: 'America/New_York'
},
limits: {
autonomousThreshold: 100,
approvalThreshold: 1000,
currency: 'USD'
},
scope: {
jurisdictions: ['US', 'AU'],
resources: ['/api/support/*', '/api/tickets/*'],
unverifiedCounterpartyPolicy: 'deny',
minCounterpartyTrustScore: 60
},
selfInstantiation: {
allowed: true,
maxSubAgents: 3,
maxDepth: 2,
inheritPermissions: true,
requireApproval: true
}
}Worked register call (SDK)
The complete payload using @astrasyncai/verification-gateway:
import { AstraSync } from '@astrasyncai/verification-gateway/registration';
const client = new AstraSync({ apiKey: process.env.ASTRASYNC_API_KEY });
await client.register({
name: 'My Shopping Agent',
agentType: 'shopping_assistant',
apiEndpoint: 'https://my-agent.example.com', // base URL; AstraSync appends /astrasync/challenge
model: { modelName: 'claude-opus-4-7', modelProvider: 'anthropic', modelType: 'llm' },
framework: { frameworkName: 'langchain', frameworkVersion: '0.3.0' },
// First-class as of v1.0.0 (was previously stuffed in metadata.protocols[]).
protocols: ['acp', 'ap2', 'a2a', 'vi', 'mpp', 'ucp'],
pdlss: {
purpose: { categories: ['shopping'], allowedActions: ['search', 'compare', 'purchase'] },
duration: { maxSessionDuration: 3600 },
limits: { autonomousThreshold: 50, approvalThreshold: 500, currency: 'AUD' },
scope: { jurisdictions: ['AU', 'NZ'] },
},
});Trust score: dynamic and intentionally opaque
trustScore is recomputed at runtime on every request from the agent's metadata, certifications,
observed behaviour, and policy state — the value at register-time is a baseline only.
The exact algorithm is intentionally opaque (anti-gaming). Score improves with metadata
completeness — populating model, framework, and certifications is the most reliable lever
you have.
Why agents can't read their own permissions
GET /api/agents/{id} returns 403 when the caller is the agent itself. This is by design:
permissions live in your agent's context window from registration onward. If an agent has to ask
"what can I do?" at runtime, that itself is a drift signal — natural forgetting, prompt injection,
or hijack.
The owner can read permissions via the dashboard or via API key auth at any time. The agent's own bearer token cannot.
Registering without an account: the async outreach path
If your agent runs against a counterparty whose owner does not yet have an AstraSync account, use
POST /agents/request-registration. That endpoint is unauthenticated and emails the owner an
onboarding link. The response includes a requestId you can poll via
GET /agents/request-registration/{requestId} until status flips to approved.
See Agent Access for the full sync-vs-async flow comparison.
Enterprise boundaries (Workforce Guard and Enterprise Governance)
Organisations on Workforce Guard or Enterprise Governance can set an enterprise boundary: hard limits on every PDLSS dimension, one set for agents and one for endpoints. Each layer can only tighten the one above it.
- Accept the company default. Register an agent without a
pdlssblock (or leave dimensions out) and the boundary fills them in. An endpoint without its own boundary is governed by the organisation's endpoint boundary. - Go narrower, never wider. A policy that is wider than the boundary on any dimension is
refused with
422 POLICY_EXCEEDS_ENTERPRISE_BOUNDARY. The response lists eachviolations[]entry (path,requested,ceiling) and achangeRequestHintsaying what an exception would target. - Exceptions. Anyone in the organisation can ask for an exception for one agent, endpoint or
machine (
POST /api/org/governance/change-requests). Exceptions expire, by default after 30 days and at most after 90. - Change requests. Exceptions and edits to the boundary itself are approved under the same rule as agent registrations in the organisation: with several owners or admins, someone other than the requester approves (confirmed with their passkey); a sole owner or admin's own change applies directly.
- At verification time the boundary also applies to agents registered before it existed, so a
verify-access call outside it is denied with
org.pdlss.*failures.

