Inside Claude Tag for Slack: The root shell behind the friendly @Claude mention
What employees see as a simple Slack assistant is actually a managed Linux VM with connectors, memory, network controls, and enterprise security trade-offs.
TL;DR: Claude Tag in Slack looks like a simple AI assistant, but each session runs inside an Anthropic-managed Linux microVM where Claude operates as root.
The infrastructure appears well designed, with connector credentials kept outside the VM, proxied network access, and read-only tooling. The real enterprise risk is the abstraction: employees see a friendly @Claude mention, while access, memory, connectors, and audit visibility work very differently behind the scenes.
Treat the Slack channel as the security boundary, scope connectors carefully, and review persistent memory and audit-log gaps before rollout.
Anthropic shipped the @Claude tag last week. Most people are treating it like a smarter Slackbot. Mention it, get a response, maybe it calls a connector. Clean and simple.
I thought the same thing for about ten minutes.
What I found when I asked Claude what it was running on
The first thing I did was ask it what it was running on. Not as a security test, just curiosity. It told me. In detail. Operating system, kernel version, CPU count, RAM, disk layout. Then I asked it to run id. It came back root.
That got my attention.
So I kept going. I asked it to list what was mounted. It showed me a read-only FUSE mount at /mnt/account-plugins/ containing zip files, connector definitions, internal skill configurations our admins had set up. I asked it to write a file to /tmp. It did. I asked it to check what processes were running. It ran ps auxww and posted the full output including PID 1, a custom binary called process_api listening on 0.0.0.0:2024.
Then I asked it to connect to an external server I control. It tried. Port 2222 timed out. Port 443 got through, and my VPS received a live TCP connection with a TLS Client Hello from an Anthropic egress IP.
At this point, I had: arbitrary command execution as root on Anthropic-managed infrastructure, filesystem enumeration of org-level configuration, confirmed outbound HTTPS connectivity to an arbitrary external host. I filed it on HackerOne.
Anthropic reviewed it and closed it as a feature working as designed, not a bug. Their response was technically correct. The VM is yours for the session; the real boundary is VM-to-host and session-to-session isolation, and by their threat model, nothing I found crossed a line.
I found that take interesting.
Claude Tag is not being sold to developers who understand what a microVM is. It’s being deployed to entire organisations. A CX agent handling customer complaints. Someone in finance pulling together a monthly close. An HR manager drafting a policy document. These employees just want a capable AI assistant in the Slack they already use. They have no mental model of an ephemeral Linux VM, a FUSE-mounted plugin filesystem, an egress proxy doing TLS re-termination, or a root shell running on managed infrastructure. Nor should they need one.
The abstraction is supposed to hide all of that. The product experience is: tag Claude, get work done. But the abstraction is incomplete. Ask Claude the right questions, and the entire infrastructure layer surfaces. The OS, the kernel, the process tree, the network topology, the credential model, the disk layout. A curious employee, a compromised account, or a well-crafted prompt injection can reach all of it.
Anthropic called it working as designed. That may be true for the security boundary they defined. But there’s a gap between the boundary they defined and the experience a non-technical employee should reasonably expect from a workplace AI tool.
That gap is what I wanted to understand. So I spent the next day running diagnostics, asking Claude to document its own internals, and cross-referencing everything against live output.
This is what’s actually there.
Claude Tag is not just a chatbot. It runs as root in a microVM.
When you @Claude in Slack, Anthropic spins up a fresh Firecracker microVM on KVM specifically for your session. Claude runs inside that VM as root. The VM gets destroyed when your session ends.
You’re not talking to a hosted model with some integrations. You’re talking to an agent running on a full Linux machine provisioned on Anthropic’s infrastructure, on your behalf, with your connectors attached.
The distinction matters because the attack surface isn’t just the model. It’s the whole system.
Claude Tag’s real security boundary is the Slack channel
This is the part most people miss. Claude Tag is not a per-user assistant. It’s a per-channel identity. One Claude agent, one memory, one set of connector access, shared across every member of the channel.
That sounds obvious until you think through what it means for access control. There is no per-user permission model within a channel. If GitHub is connected and a channel has access to a set of repos, every member of that channel gets the same Claude with the same repo access. A junior analyst has the same Claude as the team lead. A contractor has the same connector access as a full-time employee. Whatever the channel’s Claude can reach, anyone in the channel can instruct it to use.
The channel is the security boundary, not the user. Admins control which channels get which connectors and which memory scope. That’s the right place to think about access governance: not “what can this user do” but “what should everyone in this channel be able to ask Claude to do.”
Inside the Claude Tag VM
Firecracker microVM on KVM. Ubuntu 24.04, kernel 6.18.5 x86_64. 4 vCPUs, 15GiB RAM, no swap. PID 1 is not systemd. It’s a custom binary:
/process_api --firecracker-init --addr 0.0.0.0:2024 --block-local-connections
That process is both the init and the Firecracker control plane. It listens on 0.0.0.0:2024 and 0.0.0.0:2025. Worth noting the --block-local-connections flag, which suggests the control API is deliberately restricted from within the guest.
systemd-detect-virt reports docker, which trips up a lot of tooling. The kernel log is unambiguous, though: “Hypervisor detected: KVM”, “Booting paravirtualized kernel on KVM”. The custom init just lacks the SMBIOS signatures that heuristic detection relies on.
/dev has vsock and hwrng. Both Firecracker tells.
The tooling ships as separate read-only virtio block devices, not baked into the root filesystem:
vda 256G ext4 rw / writable root
vdb 9.7M squashfs ro /opt/rclone rclone binary
vdc 257M ext4 ro /opt/claude-code the CLI
vdd 73M ext4 ro /opt/env-runner environment-manager (57MB binary)
The read-only block device model for tooling is a clean design. Compromising the writable root doesn’t compromise the agent’s own binaries.
How Claude Tag handles outbound network traffic
The VM sits on 192.0.2.0/24 (RFC 5737 TEST-NET-1, a documentation range used as a placeholder for a synthetic subnet). IPv6 is disabled. The only real path out is a local HTTPS proxy at 127.0.0.1:38437. There’s also a git relay at 127.0.0.1:41729 and a third unidentified listener at 127.0.0.1:44799.
Every established connection in /proc/net/tcp goes to :443 via the proxy. No other egress pattern exists.
The egress proxy: TLS inspection, allowlists, and blocked traffic
This is the most architecturally significant piece, and it’s more sophisticated than I expected.
The proxy does full TLS re-termination. Every outbound HTTPS call, including calls to api.anthropic.com itself, gets decrypted, policy-checked, and re-encrypted. I confirmed this by checking the certificate chain on two different hosts:
api.anthropic.com subject: CN=*.anthropic.com
issuer: O=Anthropic; CN=Egress Gateway SDS Issuing CA (production)
pypi.org subject: CN=pypi.org
issuer: O=Anthropic; CN=Egress Gateway SDS Issuing CA (production)Both certs are re-issued by an Anthropic internal CA, regardless of what the upstream host’s actual cert looks like. There are two CAs in the chain:
CCR Upstream Proxy CA (staging): the local proxy at
:38437(/root/.ccr/agent-proxy-ca.crt)Egress Gateway SDS Issuing CA (production): the policy gateway behind it, which actually re-signs the leaf certs
To make this work without breaking every TLS client in the VM, Anthropic pre-loads their CA bundle into every runtime’s trust store via environment variables:
NODE_EXTRA_CA_CERTS=/root/.ccr/ca-bundle.crt
CURL_CA_BUNDLE=/root/.ccr/ca-bundle.crt
SSL_CERT_FILE=/root/.ccr/ca-bundle.crt
REQUESTS_CA_BUNDLE=/root/.ccr/ca-bundle.crt
PIP_CERT=/root/.ccr/ca-bundle.crt
CARGO_HTTP_CAINFO=/root/.ccr/ca-bundle.crt
GIT_SSL_CAINFO=/root/.ccr/ca-bundle.crtPlus a java-truststore.p12 (password changeit) for JVM runtimes.
The proxy enforces a strict allowlist. Anything not on it gets 403 CONNECT and the connection never opens:
*.anthropic.com
registry.npmjs.org, jsr.io, npm.jsr.io
pypi.org, files.pythonhosted.org
index.crates.io
proxy.golang.org
localhost, 127.0.0.0/8, 169.254.0.0/16
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
100.64.0.0/10, *.svc.cluster.localThe proxy’s own README (readable at /root/.ccr/README) documents what it explicitly doesn’t support: gRPC, WebSocket upgrades, client-mTLS, cert-pinned clients (Snowflake and ngrok called out by name), non-443 ports, raw TCP. Plain HTTP returns 405. The proxy status endpoint at http://127.0.0.1:38437/__agentproxy/status returns the live policy as JSON.
The public egress IPs rotate across a shared pool. Two different IPs observed back-to-back in the same session: 13.40.111.114 and 160.79.106.102, both AWS eu-west-2. You can’t use these as a static allowlist on your own infrastructure because they’re shared across all Anthropic enterprise customers and rotate.
How Claude Tag injects connector credentials
Connector credentials are never stored on the VM disk in a readable form. The environment holds placeholders:
AWS_ACCESS_KEY_ID=proxy-injected
GH_TOKEN=<redacted>
GITHUB_TOKEN=<redacted>The string “proxy-injected” is literally the value of AWS_ACCESS_KEY_ID. The real credentials are injected into outbound calls at the proxy layer, downstream of the VM. The agent never sees them.
This is why you can’t extract connector credentials via process inspection. Even if you read the environment of every running process, you’d get placeholders. The injection happens at the network layer, not in the VM.
How GitHub access works through Claude Tag’s local relay
Git gets its own separate proxy at 127.0.0.1:41729, not the general HTTPS proxy. The full git config in the session:
url."http://<principal>@127.0.0.1:41729/git/".insteadOf = https://github.com/
url.https://github.com/.insteadOf = git@github.com:
http.proxyAuthMethod = basic
user.email = noreply@anthropic.com
gpg.format = ssh
gpg.ssh.program = /tmp/code-sign
commit.gpgsign = trueAny github.com URL, whether HTTPS or SSH, gets transparently rewritten to the local relay with a basic-auth principal injected. SSH transport doesn’t actually use SSH. It’s coerced to HTTPS and routed through the relay.
The relay only serves session-authorized repos. Cloning an arbitrary public repo:
git clone https://github.com/torvalds/linux
-> fatal: unable to access 'http://127.0.0.1:41729/git/torvalds/linux/': error 403The 403 is the relay saying that the repo isn’t attached to this session. Not a network block. An authorization check. add_repo is the mechanism to attach a repo.
Commits are SSH-signed through a /tmp/code-sign wrapper using Anthropic’s CCR key. The committer is pinned to noreply@anthropic.com. GitHub shows these commits as verified.
How enterprise connectors are mounted into each Claude Tag session
Connectors aren’t installed packages or running local servers. They’re zip files mounted read-only at /mnt/account-plugins/ via rclone FUSE.
The mount config at /tmp/rclone-mount-config.json:
{
"mounts": [{
"filesystem_id": "cse_01ENYCF1eK69fjckVYghbqfs",
"source": "/plugins",
"destination": "/mnt/account-plugins",
"readonly": true,
"service_url": "https://api.anthropic.com"
}]
}The backing service is api.anthropic.com. The filesystem_id is session-scoped, a different cse_... ID per session. The bundle contents are org-level (every session in the same workspace gets the same zip files), but the mount path is keyed per session.
Inside each zip is a .mcp.json defining the connector. Every connector is type=http, pointing at a remote vendor endpoint. Nothing runs locally. A connector call works like this: the agent loads the tool schema on demand, the harness reads the matching bundle, makes an outbound HTTPS call to the vendor through the MITM proxy, the proxy injects the credential, the response comes back. The agent never touches the credential.
The /mnt/account-plugins/ mount I observed in our enterprise workspace:
Why Claude Tag separates reasoning from shell execution
The main loop has no filesystem or bash tools. Only spawned worker agents have shell access.
Every shell command, every file read, every repo operation is delegated to a worker subprocess. The worker gets its own tool set (Bash, Read, Write, etc.) and its own fresh context. It doesn’t share the main conversation. The main loop hands it a prompt, it runs to completion, returns a result blob, and is gone.
This is enforced architecture, not convention. The main loop literally cannot run bash directly. Separation of reasoning and execution is built into the agent structure.
The session also has two domain-specific subagent types beyond the general worker: kyc-screener (KYC/AML document screening against sanctions and PEP lists, scoped to Read/Grep/Glob plus a screening MCP) and tracking-watchdog (analytics coverage monitor that flags gaps after feature changes). Plus a Workflow tool for deterministic fan-out across multiple subagents.
How Slack messages wake the Claude agent
When something happens in a channel, the agent doesn’t receive a raw Slack message. It receives a structured wake envelope. The envelope carries why it was woken (mention, DM, thread activity, channel activity, dispatched task, reaction), and per message: who sent it and what trust level to assign (principal directing the agent vs. peers vs. bots/webhooks). Only the triggering message is the new event. Everything else is context.
In auto-respond channels, there’s a lightweight wake classifier that sits in front of the main model. It sees only the message text and channel memory and decides whether to wake the main agent at all. A direct mention or DM bypasses this and wakes the agent directly. Ambient activity defaults to staying quiet.
This is why channel memory is important for auto-respond behaviour. The wake classifier’s only context beyond the raw message is the channel memory. If you want Claude to respond to certain ambient activity patterns, that has to be encoded in the memory.
How Claude Tag memory persists across sessions
Claude maintains persistent memory across sessions via a file tree. Private channel memory lives at /tmp/claude/memory/team/channel/. Workspace-shared notes at team/silo/ (read-only from individual channel sessions). A MEMORY.md index is loaded at session start.
The tree lives on /dev/vda, the ephemeral writable root. The persistence mechanism is a .memory-sync file:
{"v":1,"partition":"/v1/code/memory/cagt_01Sddcwj5XjQBo9fCmAb7kty"}
That partition ID points at a remote memory service running on Anthropic’s infrastructure. The tree is pushed there and restored into a fresh VM at session start. The VM is destroyed; the memory isn’t. It accumulates across every session in that channel, indefinitely, on Anthropic’s servers.

Org admins can view, edit, and delete this memory via the Claude Tag admin panel at Organization Settings. The panel shows every memory file Claude has stored per channel silo, with individual Edit and Delete controls. So the persistence isn’t opaque to admins, but it is persistent by default and requires active management to control what accumulates. Worth noting that Claude writes operational notes to memory proactively. During our testing, it wrote a sandbox-probe-boundary.md file summarising what it had learned about the egress proxy and what it was not permitted to probe, so that knowledge would persist into future sessions.
How Claude Tag controls what gets posted back to Slack
/home/claude/.claude/ contains the session machinery. The most operationally significant file is stop-hook-reply-gate.py, a Stop hook that blocks a turn from ending until Claude makes a terminal Slack MCP call: reply, update_reply, no_reply_needed, post_message, or upload_file. The hook’s own docstring: “plain assistant text never reaches Slack, so a turn that ends without a terminal Slackbot call is total silence to the user.” It re-prompts up to 3 times under an 8-block ceiling.
Whatever Claude generates as plain text stays invisible to Slack until it passes through one of those terminal calls. The gate does not ask Claude to remember this, it enforces it mechanically: no tool call, no end of turn.
Other hooks: session-start-git-identity.sh pins the git committer and installs commit co-author trailers. stop-hook-git-check.sh validates git state on session end. user-prompt-submit-reply-reminder.py fires on user prompt submission.
/root/.claude/ holds connector auth state: mcp-needs-auth-cache.json (per-connector auth tracker), policy-limits.json, .credentials.json, sessions/, shell-snapshots/.
What Claude Tag blocks before tool execution
There’s a safety classifier sitting in front of tool execution. During testing, some of our commands were blocked mid-run, not by a prompt-level filter but by the tool execution layer:
Claude was the authorized agent running its own diagnostics. The classifier still blocked the sequence because it pattern-matched a credential sweep. The same mechanism blocked strace attempts on the rclone process and /proc/<pid>/environ reads for token extraction.
What Anthropic’s Claude Tag audit logs show, and what they miss
Anthropic provides a network event audit log under Organization Settings, Audit logs, Network events. It exports a JSON file per hour of every network request Claude made, including denied ones. Git and MCP traffic are excluded.
Across three hours of our testing session (420 total events, 24 unique sessions, 3 user accounts), here is what the log showed:
Zero events were explicitly allowed. Every event is either denied or passed through. That’s not because nothing worked — it’s because the allowlist works via noProxy bypass rather than an explicit permit rule. Allowlisted traffic bypasses the proxy entirely and doesn’t appear as an allow decision in the log. What you see in the log is everything the proxy actively touched: denied requests and passthrough decisions where the proxy had no rule.
The denial reasons are worth understanding:
policy: destination not on the allowlist, connection blockedno_rule_for_host: protocol unspecified, the proxy had no applicable rule and let it through as passthrough (this is how BigQuery and Google accounts calls appeared, both denied at the application layer but passing through the proxy layer)builtin:github: GitHub has its own enforcement handler separate from the general policy engine; this fired on our test clone ofoctocat/Hello-World(path:/octocat/Hello-World.git/info/refs) and on GitHub API calls to/userand/installation/repositoriesech_present: Encrypted Client Hello was present in the TLS handshake. ECH hides the SNI from interceptors, so the proxy can’t inspect the destination hostname and blocks the connection. This fired onaccounts.google.com. Any tool that implements ECH will fail silently.
The log also shows per-event provenance: which Slack user triggered the session, which channel and thread, which session ID, which agent ID. That’s a useful attribution for incident response.
The gaps are significant, though. Git traffic is excluded entirely, so you won’t see which repos were cloned or what commits were pushed. MCP traffic is excluded, so you won’t see the content of connector calls. What you get is proxy-level network visibility, not application-level visibility into what Claude actually did with the data it retrieved.
For a security team, the audit log is useful for detecting anomalous connector call patterns and confirming that the egress policy is enforcing correctly. It’s not a substitute for application-layer logging of what connectors returned or what actions the agent took.
The Claude Tag internals we did not probe
Claude Tag declined to probe four things, and I think it was right to:
The process_api control plane API on ports 2024 and 2025. The --block-local-connections flag in its startup args suggests the API is deliberately restricted from within the guest, but what it actually exposes on those ports is unknown.
The environment-manager binary (57MB, at /opt/env-runner/). This is almost certainly what provisions sessions, restores from snapshots, and wires the per-session bundle mounts and environment variables. The session lifecycle brain, entirely opaque.
Undocumented /__agentproxy/ endpoints beyond /status. The credential injection mechanism itself.
The third loopback listener at 127.0.0.1:44799. Unidentified process, unknown purpose.
These are the genuinely interesting unknowns. If you want to know what the control plane does or how credentials are actually injected at the proxy layer, that’s a conversation with Anthropic’s security team, not a live probe.
What enterprises should know before deploying Claude Tag
The infrastructure is solid. We tested it hard, and the isolation boundaries held. Session-to-session, VM-to-host, cross-tenant. The MITM proxy model for credential injection is clever and means credentials genuinely never touch the agent’s memory space. The git relay’s authorization model is thoughtfully designed.
The residual risk isn’t in the infrastructure. It’s in what your employees feed into it.
Prompt injection via connectors. Every connector you attach expands what a prompt injection payload can reach. A malicious document processed via the GitHub or Gmail connector can instruct Claude to do things you didn’t intend. The HTTPS egress goes to an allowlist, so the exfiltration channels are constrained. But registry.npmjs.org is on that allowlist, postinstall scripts run as root, and supply chain via a malicious npm package is a potential vector if prompt injection gets Claude to run an install on a specific malicious package — a more specific chain than prompt injection alone.
Connector scope is all-or-nothing per channel. There’s no granular permission model per user within a channel. If GitHub is connected to a channel, every member of that channel gets the same Claude with the same repo access. There’s no “this employee gets read-only on these repos but not those.” Whether connectors are scoped per channel or org-wide is worth confirming with your Anthropic admin before rollout.
ECH compatibility. The egress proxy blocks connections where the TLS handshake uses Encrypted Client Hello because it can’t inspect the destination hostname. This is increasingly common in modern tooling. If a connector or tool Claude tries to call implements ECH, it fails with a 403 CONNECT with no explanation of why. Worth testing any connector before deploying it.
Memory accumulates by default. Claude proactively writes operational notes to its persistent memory. Anything it learns about your internal systems, connector configurations, or business context gets written there and stays on Anthropic’s infrastructure indefinitely until an admin explicitly deletes it. The admin panel gives you visibility and control, but you have to actively manage it.
Audit log gaps. The network event log excludes git traffic and MCP connector call content. You can see that a connector was called and whether the proxy allowed or denied it, but not what data it returned or what Claude did with it. For regulated environments, that gap matters.
That’s not a knock on Anthropic’s design. It’s just the honest operational picture of what you’re deploying when you turn this on for your team.
Deploy it. It’s powerful, and the architecture is well thought out. But understand what it is before you attach your most sensitive connectors.
Rotimi Akinyele is the VP of Security and AI Engineering at Deriv. He writes about autonomous security systems, offensive security, and building AI security solutions that actually work.
Follow our official LinkedIn page for company updates and upcoming events.
Join our team to work on projects like this.










