Addressing
Every ANCP sender and receiver is identified by a structured URI address — encoding the scheme, tenantId, and entity path in a single string that routers can parse and validate.
Address Format
ANCP addresses follow a URI-style format:
{scheme}://{tenantId}/{...entity-path}
| Part | Role | Example |
|---|---|---|
scheme | Identifies the type of addressable entity | node, agent, service, topic, user |
tenantId | Tenant scope — must match the envelope tenantId | tenant-acme, tenant-globex |
entity-path | Hierarchical path identifying the specific endpoint | flow-42/step-3, octopus/hr-agent |
The tenantId appears in both the address URI and the envelope's top-level tenantId field. The router compares them. If they differ, the message is rejected with a 403 and a cross-tenant violation is logged in the audit trail.
Address Schemes
node://
Addresses a specific execution node within a workflow run. Node addresses include the flow ID and, optionally, the step ID.
node://{tenantId}/{flowId}/{nodeId}
# Examples
node://tenant-acme/flow-42/approval-gate
node://tenant-acme/flow-42/send-email
node://tenant-acme/flow-55/data-transform-step-3
agent://
Addresses an AI agent managed by the Octopus framework. The agent path identifies the agent pool and the specific agent instance.
agent://{tenantId}/{agentPool}/{agentId}
# Examples
agent://tenant-acme/octopus/hr-policy-agent
agent://tenant-acme/octopus/expense-classifier
agent://tenant-acme/octopus/document-extractor
service://
Addresses a backend service endpoint (not a workflow node or AI agent). Used for inter-service Commands in the backend layer.
service://{tenantId}/{serviceName}
# Examples
service://tenant-acme/payroll-service
service://tenant-acme/notification-service
service://tenant-acme/audit-log-service
topic://
A pub/sub topic address — used exclusively in the destination field of Event type messages. Not a point-to-point address; the router publishes to all subscribers matching the topic pattern.
topic://{tenantId}/{domain}/{event-name}
# Examples
topic://tenant-acme/expenses/approved
topic://tenant-acme/workflow/completed
topic://tenant-acme/system/health-check
user://
Addresses a specific authenticated user session. Used when an agent or node wants to send a message directly to a user's browser client (e.g., a notification or form render request).
user://{tenantId}/{userId}/{sessionId}
# Examples
user://tenant-acme/user-88/sess-xyz-001
user://tenant-acme/user-42/sess-abc-002
The Role of tenantId
The tenantId segment in every address is the first and strongest isolation boundary in ANCP. Routers evaluate three conditions on every message:
Envelope tenantId matches Source address tenantId
The tenantId in the envelope's top-level field must equal the tenantId segment of the source URI.
Envelope tenantId matches Destination address tenantId
The tenantId in the envelope must equal the tenantId segment of the destination URI.
tenantId matches Authentication claim
The tenantId must match the tenant claim in the bearer JWT (for SignalR) or the API key scope (for HTTP). This prevents a tenant from forging another tenant's ID.
A message that passes authentication but fails tenant consistency is not returned to the sender as an error (to prevent information leakage). It is silently discarded and a CROSS_TENANT_VIOLATION audit event is written to the security log with the offending message ID and source address.
Routing by Address Type
| Destination Scheme | Message Type | Router Behaviour |
|---|---|---|
node:// | Command, Query, Response | Looks up the active transport for that node instance. Delivers to the node's message handler. |
agent:// | Command, Query, Response | Dispatches to the Octopus agent pool. The pool manager routes to the specific agent or a free instance. |
service:// | Command, Query | Resolves service name to HTTP endpoint. Posts the ANCP envelope as JSON body. |
topic:// | Event only | Publishes to EdgeStream pub/sub. All subscribers matching the topic pattern receive a copy. |
user:// | Command, Event | Delivers to the user's active SignalR connection identified by sessionId. |
Address Examples by Context
# Workflow node sending a command to the next step
source: "node://tenant-acme/flow-42/step-2-validator"
destination: "node://tenant-acme/flow-42/step-3-notifier"
# Node asking an AI agent a question
source: "node://tenant-acme/flow-42/policy-check"
destination: "agent://tenant-acme/octopus/policy-agent"
# Agent publishing a result event
source: "agent://tenant-acme/octopus/classifier"
destination: "topic://tenant-acme/classification/completed"
# Backend service sending notification to user
source: "service://tenant-acme/approval-service"
destination: "user://tenant-acme/user-88/sess-xyz-001"
Programmatic Address Construction
Use the ANCP address builder helpers to construct addresses safely — never concatenate strings manually, as it risks introducing malformed addresses or tenant mismatches.
import { AncpAddress } from '@bizfirst/ancp-sdk';
// Build a node address
const nodeAddr = AncpAddress.node({
tenantId: 'tenant-acme',
flowId: 'flow-42',
nodeId: 'approval-gate'
});
// => "node://tenant-acme/flow-42/approval-gate"
// Build a topic address
const topicAddr = AncpAddress.topic({
tenantId: 'tenant-acme',
domain: 'expenses',
eventName: 'approved'
});
// => "topic://tenant-acme/expenses/approved"
// Parse an address back to components
const parsed = AncpAddress.parse('agent://tenant-acme/octopus/hr-agent');
// => { scheme: 'agent', tenantId: 'tenant-acme', path: ['octopus', 'hr-agent'] }