← Bursar knowledge base

Bursar FAQ

Bursar is the Blacksands zero-trust MCP server (@blacksandscyber/mcp-server-bursar, this repo: mcp-server-blacksands-shield/). It exposes 55 tools across 9 categories to any MCP client (Claude Code, Claude Desktop, or another MCP-compatible agent) for codebase security scanning, app/org provisioning, mTLS certificate management, compliance reporting, security operations, and Receiver (edge-proxy) lifecycle management.

This FAQ is the companion to the training guide, which walks through every tool with example calls/responses. Use this doc to answer "why does X behave this way" and "what do I do when Y goes wrong" questions; use the training guide to look up a specific tool's inputs/outputs.

This is developer-focused material (tool behavior, auth model, troubleshooting). For account, plan, or billing questions, see the live Bursar AI Agents knowledge base instead.

Terminology used throughout: Agent = the MCP client calling Bursar tools (e.g. Claude). Authorizer = the first mTLS hop, issues a session token. Receiver = the second mTLS hop, a public-IP edge proxy that forwards to the real backend. Backend = the actual protected service; never directly reachable.


Table of contents


What is Bursar?

Q: What does Bursar actually do? Bursar is an MCP server that lets an AI agent drive Blacksands zero-trust security operations directly — no dashboard clicking required. It can scan a local codebase for security risk, provision an application into Blacksands (certs, DNS, policies), run compliance reports, rotate or revoke certificates, manage Receivers (the edge proxies that front protected services), and even provision a brand-new agent identity on a remote host.

Q: Is Bursar the same thing as "Shield"? Yes, historically. The repo folder and some internal docs still say mcp-server-blacksands-shield / @blacksandscyber/mcp-server-shield, but the published package and all current tool names use the Bursar prefix (bursar_*, receiver_*, broker_*). If you see "Shield" in an older doc or path, it refers to the same system.

Q: Do I need a Blacksands account to use Bursar? No — 14 of the 55 tools are [FREE] and work with zero configuration: local codebase/environment scanning, framework/PII/endpoint/service/datastore detection, manifest generation, deployment guidance, and identity/protocol introspection. The other 41 tools require broker authentication (an mTLS cert + password, issued by a human admin through Overwatch/SysAdmin, or redeemed from a one-time bootstrap token). See How does the auth model work?.

Q: What are the 9 categories mentioned in the training guide?

CategoryTool countTheme
A — Codebase & Environment Scanning10Local, mostly [FREE]; scan code/infra, generate manifests
B — App & Org Provisioning7Create orgs/apps, submit manifests, provision the security stack
C — mTLS Certificate Management (MCP identities)2List/revoke the agent's own MCP client certs
D — Verification & Compliance4Posture scoring, SOC2/HIPAA/PCI-DSS/ISO27001 reports
E — Security Operations & Cert Management (apps)10Lockdown, per-app certs, policies, DNS rules, sessions
F — Receiver Lifecycle & Service Onboarding13Stand up a Receiver, onboard/pause/disable proxied services
G — Receiver Monitoring3Health, listing, fleet-wide statistics
H — Composite Remote Agent Installation1Provision + install a new agent identity on a remote host
I — Local Guidance & Learning4[FREE]; deployment guide, protocol walkthrough, identity

Full per-tool detail (inputs, outputs, gotchas) lives in the training guide, organized by these same 9 categories.


How does the auth model work?

Q: What is the "bisected mTLS broker" pattern? Every authenticated call goes through two independent mTLS handshakes, not one:

  1. Agent → Authorizer. The agent presents its mTLS cert + password to the Authorizer (SHIELD_AUTHORIZER_URL, e.g. mauth.<your-org-domain>). The Authorizer validates the cert against the org's enrolled identities and returns a service list with a Receiver URL + short-lived session token.
  2. Agent → Receiver. The agent presents the same cert again (a second, independently verified handshake) plus the session token to the Receiver — a public-IP edge proxy. The Receiver re-verifies the cert on its own, then forwards the request to the actual backend service over a private network.

The backend service is never directly reachable by the agent. This is unusual compared to common mTLS patterns (Cloudflare Access, single-stage client-cert auth) — call broker_get_protocol_walkthrough before writing any client code so you don't assume a single-handshake model.

Q: Why two handshakes instead of one? Defense in depth: a compromised or spoofed Authorizer can only hand out a token — it cannot itself reach the backend. The Receiver independently re-verifies the cert, so a stolen token alone (without the matching cert) is useless, and a compromised Authorizer alone cannot forge Receiver access.

Q: What credentials does an agent need, and where do they come from? An mTLS client cert + key, an auth password, and an org id. These are either:

  • issued by a human administrator through Overwatch or SysAdmin, and passed via SHIELD_CLIENT_CERT / SHIELD_CLIENT_KEY / SHIELD_AUTH_PASSWORD / SHIELD_ORG_ID; or
  • redeemed automatically on first launch from a one-time SHIELD_SETUP_TOKEN (a bss_... token), after which the bundle is persisted to ~/.blacksands/mcp-certs/; or
  • provisioned programmatically for a new agent identity via bursar_install_agent_remotely (Category H), which issues a fresh bootstrap token for a target host to redeem.

There are no API keys and no bearer-token fallback — Bursar is cert-only. Fallback auth chains were deliberately stripped from every deployed service (see PRs #587–#595 in the Blacksands history); do not reintroduce one.

Q: What's the difference between "master" and "consumer" role? master role sees all 55 tools (subject to which are gated). consumer role is a least-privilege mode that sees only the 14 [FREE] tools plus the ability to go through the broker for whatever services it's been granted — it cannot provision, revoke, or manage anything. Role is set at cert issuance time (or via the role input on bursar_install_agent_remotely); an agent cannot self-escalate.

Q: What happens if I call a gated tool without credentials (local-only mode)? You get a friendly setup-token prompt, not a stack trace or a generic 401. The MCP server detects it's running in local-only mode (no cert configured, or MCP_TRANSPORT=local-only) and short- circuits gated tools before attempting any network call.

Q: Is there a session token I need to manage myself? No — the session token returned by the Authorizer is ephemeral and consumed internally by the tool implementation for the Receiver hop. If you're writing your own client code outside the MCP tool layer (e.g. to call a Receiver-proxied service directly), see the code examples returned by broker_get_protocol_walkthrough.


Getting started

Call these [FREE] tools first, in this order, before doing anything else:

  1. broker_get_my_identity — confirms who you are (cert CN, role, org id, tier) and whether you're in local-only or broker mode. In local-only mode this returns a synthetic identity with role: "consumer" rather than erroring, so it's always safe to call first.
  2. broker_get_protocol_walkthrough — the authoritative description of the bisected auth chain, with copy-paste Node/Python/curl examples. Call this before generating any client code that talks to a Blacksands-protected service directly (outside the MCP tool layer) — do not let an LLM improvise the auth flow from generic mTLS training data.
  3. bursar_guide_deployment (no target argument) — the authoritative deployment architecture overview, plus a picklist of supported targets (digitalocean-droplet, local-docker-development). Call this before answering any deployment/hosting question; call it again with a specific target for copy-paste steps.
  4. bursar_scan_codebase — if you're onboarding an actual application, this is usually the first substantive action: a full composite local scan (framework, endpoints, PII, external services, data stores) that also assembles a security manifest.
  5. bursar_get_protection_requirements — feed it the manifest (or a project path) to get a plain-English checklist of what infrastructure needs to exist (a deployed backend + Receiver) before Blacksands can protect the app, and whether you need a paid tier for any next step.

From there, the typical provisioning path (Category B) is: bursar_create_org → bursar_create_app → bursar_generate_manifest → bursar_submit_manifest → bursar_provision_app (async — poll with bursar_poll_operation).

The typical Receiver setup path (Category F) is: receiver_initialize → (installer runs the Docker bootstrap command on the target host) → receiver_init_status (poll) → receiver_activate → receiver_onboard_service per service you want proxied.


Tool categories at a glance

CatName[FREE] toolsGated tools
ACodebase & Environment Scanning10bursar_publish_environment requires master + paid tier
BApp & Org Provisioning0all 7
CmTLS Certificate Management (MCP identities)0both
DVerification & Compliance0all 4
ESecurity Operations & Cert Management0all 10
FReceiver Lifecycle & Service Onboarding0all 13
GReceiver Monitoring0all 3
HComposite Remote Agent Installation0the 1 tool
ILocal Guidance & Learning40

See the training guide for the per-tool breakdown within each category.


Gotchas and notable behaviors

This section is one FAQ entry per notable behavior surfaced during tool research. If a tool isn't mentioned here, its behavior is unsurprising — check the training guide for details.

Scanning (Category A)

Q: bursar_scan_codebase is slow on a large repo — is that expected? Yes. It walks the full project tree and runs pattern matching for every sub-scan (endpoints, PII, services, data stores) in one pass. 10–30 seconds on a large codebase is normal; there's no incremental/cached mode yet.

Q: bursar_scan_environment's zt (zero-trust) plane came back empty — is that a bug? No. The zt plane is only populated from live Shield broker state (receivers/sessions/ endpoints). If the broker is unreachable (local-only mode, or network issue), the plane is legitimately empty and metadata.trust.authoritative is false — the tool is telling you the data isn't authoritative, not silently failing.

Q: PII/endpoint/service detection found something that isn't actually there (false positive) — is that expected? Yes, by design. bursar_flag_pii_candidates and bursar_scan_endpoints are heuristic scanners: findings are candidates for human review, not confirmed facts. Confidence is reported per finding (high/medium/low); comment-only or prose-only matches are filtered to cut noise, but false positives still happen. Don't auto-remediate based solely on these scans.

Q: bursar_report_activity and bursar_show_topology didn't do anything I could see — is the tool broken? Probably not. Both are purely presentational, targeting a local visualization launcher at http://127.0.0.1:4178. If that launcher isn't running (and can't be auto-spawned, or you're in a remote/sandboxed agent context), bursar_show_topology returns launched:false, opened:false with a note to open the URL on your workstation, and bursar_report_activity falls back to a journal file at ~/.blacksands/container-manager/activity.jsonl. Neither tool ever throws, and neither can change topology, trust, certs, sessions, or any Blacksands state — they're cosmetic.

Q: Why did bursar_publish_environment reject my call with an upgrade-path error? It requires master role and a paid tier (essentials/professional/enterprise) — community and trial orgs get an upgrade-path error instead of a scan. Use bursar_scan_environment (fully [FREE], local-only) if you just need the topology without cloud publishing/history retention.

Provisioning (Category B)

Q: bursar_provision_app returned right away with a status — do I need to poll? The public contract is async: bursar_provision_app internally uses bursar_poll_operation to wait for completion before returning, so a synchronous-looking "status": "completed" response is normal for typical manifests. For larger/slower provisions, or if you're building your own client around the underlying operation, be prepared to poll bursar_poll_operation yourself using the returned operationId. Polling is blocking on the caller's side — implement backoff, don't hot-loop.

Q: bursar_submit_manifest failed with validation errors — what does that mean? The Shield API schema-validates every manifest before accepting it. Validation errors block submission entirely; validation warnings (e.g. "PII detected in endpoints; ensure encryption in transit") don't block, but flag compliance follow-ups. Fix errors and resubmit; warnings are informational.

Q: Org creation failed with a duplicate-name error. Org names must be unique across Blacksands. Use bursar_list_orgs to check existing names before calling bursar_create_org.

Q: My trial-plan org hit a hard limit (receivers/apps/time). Trial plans are capped: 1 Receiver, 3 apps max, 30-day limit. This is enforced server-side, not a client bug — upgrade the org's plan to lift the caps.

Certificates and sessions (Categories C, E)

Q: What's the difference between bursar_revoke_mcp_cert (Category C) and bursar_revoke_cert / bursar_rotate_cert (Category E)? bursar_revoke_mcp_cert (Category C) revokes an MCP client identity — the cert an agent uses to talk to Bursar at all. bursar_revoke_cert / bursar_rotate_cert (Category E) act on app-level certificates — mTLS certs tied to a provisioned application's Receiver/backend traffic. Don't confuse the two: revoking the wrong one either locks out your own agent or breaks an unrelated app's connectivity.

Q: I revoked an MCP cert but the client using it still seems to work. Revocation via bursar_revoke_mcp_cert is immediate on the server side, but existing sessions are not forcibly terminated — they expire naturally on their own TTL. If you need an immediate cutoff, use bursar_emergency_lockdown (Category E) on the affected app instead, which does forcibly terminate active sessions.

Q: bursar_rotate_cert broke my running app/agent immediately — is that expected? Yes. Rotation is synchronous: the old cert is revoked the instant the new one is issued, with no grace period. Anything still using the old cert loses connectivity right away and must be updated with the new cert/key. If you need a grace period, use bursar_revoke_cert on a schedule of your choosing instead of rotating in place.

Q: Is revoking a cert or session reversible? No — bursar_revoke_cert, bursar_revoke_session, and bursar_revoke_mcp_cert are all immediate and irreversible. There's no "undo"; you'd need to provision/rotate a new cert/session. bursar_revoke_mcp_cert is idempotent (calling it twice on an already-revoked cert returns the same result, it doesn't error).

Q: What actually happens during bursar_emergency_lockdown, and how do I undo it? Lockdown revokes all certs and forcibly terminates all sessions for the target app — immediately, and it's idempotent (locking an already-locked app is a no-op). bursar_lift_lockdown returns the app to active status, but it does not restore anything that was revoked — those certs/sessions stay revoked permanently. You must re-provision (bursar_provision_app or bursar_rotate_cert) to get a working cert bundle again after lifting a lockdown. Reserve this tool for genuine incidents; the reason string is logged for the compliance audit trail.

Policies and DNS (Category E)

Q: My policy isn't behaving the way I expected — I have both an allow and a deny rule for the same target. Policy rules are evaluated left-to-right / in array order — the first matching rule wins. Order your rules array with the most specific matches first. The same first-match-wins semantics apply to bursar_update_dns_rules.

Q: I called bursar_update_dns_rules but the app's inbound DNS didn't change. bursar_update_dns_rules only manages application-level egress rules (outbound DNS lookups the app is allowed to make). Receiver DNS (ingress) — the public hostname clients use to reach the app — is auto-managed by Blacksands and cannot be edited through this tool; see receiver_get_dns (read-only) instead.

Verification & compliance (Category D)

Q: bursar_verify_posture with scanType: "full" is taking minutes — is it hung? Not necessarily. quick scans return in ~2–3 seconds; full (the default) can take 5–10 minutes because it's an async operation that polls internally (bursar_poll_operation) and exercises checks against the Receiver + backend. If a downstream service is unreachable, that check is skipped rather than failing the whole scan.

Q: bursar_get_posture returned stale-looking data. By design — bursar_get_posture returns the cached result of the most recent bursar_verify_posture run; it never triggers a new scan itself. If no scan has ever run for that app, expect a null/error response. Call bursar_verify_posture first if you need fresh data.

Q: My compliance report shows controls as "partial" — what does that mean? bursar_compliance_report control statuses are pass, fail, partial, or not_applicable. partial means the control is partly satisfied (e.g. data-in-transit is encrypted but at-rest isn't) — check the evidence field per control for specifics. Reports are valid for ~90 days; regenerate after remediation or near expiry.

Receiver lifecycle (Categories F, G)

Q: receiver_initialize succeeded but nothing is running yet. That's expected — receiver_initialize only creates the Receiver record and emails a one-time setup token (valid ~2 hours) to the installer. A human still has to run the Docker bootstrap command on the actual target host (a Linux server with Docker — EC2, VPS, DigitalOcean droplet, etc.). Poll receiver_init_status to watch progress once that's underway.

Q: My setup token/email expired before the installer ran the bootstrap command. Setup tokens are short-lived (~2 hours for Receiver init, ~15 minutes for bursar_install_agent_remotely's bootstrap tokens). Use receiver_resend_email to issue a fresh token for a still-awaiting Receiver init; for an expired agent-install bootstrap token, re-run bursar_install_agent_remotely.

Q: receiver_activate says it's a no-op — did something break? No — activation is idempotent by design; calling it on an already-active Receiver just returns the current state. Activation is only possible once initialization has reached status: "completed"; calling it earlier is the actual error case.

Q: What's the difference between pausing, disabling, and removing a service (Category F)? Three distinct strengths, all reversible except removal:

  • bursar_service_pause — stop accepting new connections; existing ones stay open. Good for maintenance windows. Reverse with bursar_service_resume (idempotent — resuming an already-active service is a no-op).
  • bursar_service_disable — stronger: existing connections are severed immediately, not just blocked for new ones. Requires an explicit re-enable to recover.
  • receiver_remove_service — deregisters the service entirely; its certificate is revoked asynchronously (10–30 seconds). There's no "undo" — you'd re-onboard with receiver_onboard_service.

Q: receiver_onboard_service returned a public URL, but it's not reachable yet. DNS propagation for the newly assigned subdomain can take 5–10 seconds after the call returns. Also double-check protocol: "ssh" and "rdp" are forwarded directly (not HTTP-wrapped), so path is meaningless for those and only applies to "http"/"https".

Q: bursar_service_status shows the upstream as unreachable, but the service still shows as active — is that a bug? No — an unreachable upstream is logged, not treated as an error state. The Receiver keeps accepting connections and retries health checks (every 10–30 seconds); it doesn't automatically flip the service to paused/disabled. If you want it taken offline, do that explicitly with bursar_service_pause or bursar_service_disable.

Q: Can I change a Receiver's DNS name? No — receiver_get_dns is read-only from the agent's perspective. DNS is auto-provisioned at Receiver registration; changing it requires re-registering the Receiver (a new receiver_initialize call), not an edit.

Remote agent install (Category H)

Q: I tried bursar_install_agent_remotely with transport.type: "ssh" or "aws-ssm" and got a "not-implemented" error. Expected in the current phase (Phase A.1) — only transport.type: "bootstrap-token" is fully implemented. SSH and AWS SSM transports are defined in the schema but stub out with a "not-implemented" throw; don't route users toward them yet.

Q: If something fails partway through bursar_install_agent_remotely, do I end up with an orphaned cert? Not by default — rollbackOnFailure defaults to true, so if delivery, config write, or restart fails after the credential was already issued, the tool revokes that cert automatically. Set rollbackOnFailure: false only if you specifically want to keep a partially-provisioned identity around for manual cleanup.

Q: waitForHandshake is true by default — what does it actually wait for? In the current phase it's a no-op — there's no real handshake check implemented yet (handshakeStatus in the response is always "pending"). Don't build logic that blocks on this field flipping to a "confirmed" state; it doesn't yet.

Guidance tools (Category I)

Q: bursar_guide_deployment insists Blacksands is "NOT like Cloudflare Tunnel / Tailscale / ngrok" — why does it matter? Because the Receiver model is inbound, not outbound: it requires an inbound public IP with TCP 443 open (mTLS termination), and the control plane (policy/DNS/cert-rotation pushes from Manager) is Kafka, not HTTP polling by the Receiver. If you generate deployment instructions or firewall rules from generic "reverse tunnel" assumptions, you'll get the security model wrong — that's why this tool front-loads the "false models to reject" list.

Q: Why can't I run a Receiver on my laptop for local dev? Laptops typically don't have a public IP, and the Receiver needs one for the inbound mTLS edge. bursar_guide_deployment's local-docker-development target is dev-only — you run your app locally and test through other means (e.g. the Blacksands CLI); production paths (like digitalocean-droplet) are what actually stand up a reachable Receiver.


Troubleshooting

Auth failures

SymptomLikely causeFix
Gated tool call returns a setup-token prompt instead of runningYou're in local-only mode (no cert configured, or MCP_TRANSPORT=local-only)Configure SHIELD_CLIENT_CERT/SHIELD_CLIENT_KEY/SHIELD_AUTH_PASSWORD/SHIELD_ORG_ID, or set SHIELD_SETUP_TOKEN for one-time bootstrap
"Certificate verification failed" when the tool talks to the ReceiverBlacksands CA cert missing/expired locallyConfirm ~/.blacksands/mcp-certs/blacksands-ca.crt exists and is current; override with SHIELD_CA_CERT if needed
"Unauthorized" / "Invalid token" from the ReceiverSession token expired, or the cert presented to the Receiver doesn't match the one the Authorizer issued it forRe-authenticate with the Authorizer to get a fresh token; make sure the same cert is reused for both the Authorizer and Receiver hops
broker_get_my_identity returns cn: nullBroker handshake failedNot itself an error — it's included in the identity response rather than thrown. Check warning field in the response and the auth-failure causes above
Everything worked yesterday, fails today with no config changeMCP cert may have been revoked (bursar_revoke_mcp_cert) or naturally expiredCheck bursar_list_mcp_certs for status; re-issue via Overwatch/SysAdmin or a fresh bootstrap token

Timeouts / hangs

SymptomLikely causeFix
bursar_verify_posture (full scan) seems to hang for minutesExpected — full scans take 5–10 minutes and poll internallyUse scanType: "quick" (~2–3s) if you need a fast signal instead
bursar_provision_app / any async op seems slowProvisioning is async under the hoodIf you're driving the underlying operation yourself, poll bursar_poll_operation with the returned operationId and implement backoff — don't hot-loop
bursar_scan_codebase takes 10–30s on a big repoExpected — full tree walk + multi-scan pattern matchingNo incremental mode currently; scope projectPath to a subdirectory if you only need part of a monorepo scanned
A new service isn't reachable right after receiver_onboard_serviceDNS propagation lagWait 5–10 seconds and retry; check receiver_list_services for health status

Permission errors

SymptomLikely causeFix
Consumer-role agent can't see/call a tool you expectedconsumer role is restricted to the 14 [FREE] tools + broker access to granted servicesUse a master-role cert for provisioning/admin tools, or confirm this agent is intentionally least-privilege
bursar_publish_environment rejected with an upgrade-path errorTrial/community org, or non-master certUpgrade the org's plan (essentials/professional/enterprise) and/or use a master-role cert; or just use the [FREE] bursar_scan_environment if you don't need cloud publishing
bursar_create_app/bursar_create_org rejectedTrial-plan limits (1 receiver / 3 apps / 30 days) or duplicate org nameCheck bursar_list_orgs/bursar_list_apps first; upgrade plan if limits are the blocker
SSH/AWS-SSM transport on bursar_install_agent_remotely errors "not-implemented"Those transports are stubs in the current phaseUse transport.type: "bootstrap-token" instead

Where to look next

  • Full protocol reference: call broker_get_protocol_walkthrough (or see the training guide's Category I section) for the authoritative auth-chain description and code samples.
  • Deployment architecture questions: call bursar_guide_deployment before answering — don't improvise from generic tunnel/VPN mental models.
  • Per-tool inputs/outputs/gotchas: see the training guide.
  • Server logs: stderr only (stdout is reserved for the MCP wire protocol). macOS Claude Desktop: ~/Library/Logs/Claude/mcp-server-Bursar.log. Claude Code: run with --mcp-debug.