Self-hosted & on-premise
The same binary, inside your own network.
attest_tag is open source under the MIT licence: one Go binary with the admin console embedded, one database, and signed HTTPS delivery from Slack and Microsoft Teams. Run it on a laptop with Docker in a few minutes, on Cloud Run, Fargate, Container Apps or Kubernetes with the script in the repository, or hand the install to a coding agent. Nothing about it is a lesser edition — it is the code the hosted service runs.
MIT licenceone binary, console embeddedyour model endpointyour keys
What stays inside
Where everything lives when you run it.
The on-premise question is six questions. Each answer is a mechanism the rest of this site explains; this is the checklist.
Threads stay in Slack, or in your database
A Slack thread is rebuilt from Slack on every turn rather than mirrored into a second copy of your workspace. Teams gives an app no way to read a thread back, so there the bot keeps its own record — in your database, on your infrastructure.
Documents are yours to point at
A folder, a bucket, or a shared Drive folder mirrored in. The chunks and the embeddings live in your database, and a document can be scoped to one channel.
Credentials are sealed with a key you hold
AES-256-GCM under a MASTER_KEY in your environment, injected by the proxy at the network edge, scrubbed back out of every response. A copy of the database is not a copy of your credentials.
The model call goes where you point it
OpenRouter, a vendor API, or a model you host, through the OpenAI-compatible API. Point it at your own host and nothing leaves the network at all.
The audit trail is a table in your database
Every turn, tool call, proxied request and completion — with tokens and cost — stored where you can query it, and exportable as CSV from the console.
Sign-in you control
Slack or Microsoft sign-in, or email and password with TOTP two-factor, admin, editor and viewer roles, invites that expire. ALLOWED_EMAIL_DOMAINS keeps guests and Slack Connect members out of the bot entirely.
Quick start
Four commands, one container, a public address.
That is a working deployment: one container, one volume, and a public HTTPS address with no account and no domain — the quick tunnel hands you one.
Then make your own Slack app. It belongs to your workspace and its token never leaves your deployment: paste the manifest from deploy/slack/manifest.json, point it at that address, and install it. Open the console and sign up; the first sign-up founds the deployment, and everybody after arrives by invitation.
On Microsoft Teams, instead or as well: register an Azure Bot — the free F0 tier — with its messaging endpoint at /msteams/messages on that address, and set the four MSTEAMS_ variables. guide/msteams.md in the repository walks through it.
The one thing that trips people up: Slack will not deliver events to localhost, and there is no Socket Mode fallback, so a public HTTPS address is required rather than convenient.
git clone https://github.com/Attest-Tag/attesttag && cd attesttag ./deploy/local/bootstrap.sh docker compose --profile quicktunnel up -d docker compose logs quicktunnel # the https URL for your Slack app
bootstrap.sh writes .env and generates the MASTER_KEY, then tells you to back it up. Do that before anything else.
Platforms
One folder per place you can run it.
Each folder in deploy/ holds a README that covers that platform end to end and a script that is idempotent: rerunning it is how you ship a new version. Storage is local by default — a database and a folder of documents on a volume — and both can be something you bring: DATABASE_URL for any Postgres, DOCS_S3_URL for any S3-compatible bucket, which is what you move to when one instance is not enough.
deploy/localdeploy/gcpdeploy/awsdeploy/azuredeploy/helmSQLite needs a local block device and exactly one writer — a Docker volume, an EBS disk, a Fargate task’s own storage or a Kubernetes RWO claim are fine; NFS, EFS and Azure Files are not. The fix-job worker is one WORKER_MODE and one script per platform.
With a coding agent
Or hand the install to an agent.
Clone the repository, open Claude Code, Codex, Cursor or any agent that runs shell commands in that directory, and say where you want it. The agent reads AGENTS.md, which sends it to the runbook — about ten minutes, most of it waiting for a container to build.
Three things need a human, and an agent that claims otherwise is wrong: creating the Slack app in your workspace (or the Azure Bot, for Teams), signing in to your cloud, and backing up the master key. The runbook stops for each.
git clone https://github.com/Attest-Tag/attesttag && cd attesttag # then, to the agent, in that directory: # Read AGENTS.md, then install this on Google Cloud.
Rules the runbook gives it
- Never print a secret, paste one into chat, or commit one. .env is gitignored and values are written with a shell redirect.
- Stop before creating anything billable and show the list first.
- Never deploy to a project or account you did not name — not the CLI's currently selected one, unless you said so.
- Never run the GCP scripts against the maintainer's hosted service; a self-host passes its own env file.
- Stop and report rather than improvise when a script fails.
Agents are not bound by a document. Run it where you can see what it does, and read the confirmation prompts rather than clicking through them.
What it needs
Three things decide whether a deployment works.
They are the same three on every platform, and every folder's README says so before anything else.
A public HTTPS origin
Slack will not deliver events to localhost and will not accept an http redirect URL, and there is no Socket Mode fallback; Teams delivers to an HTTPS messaging endpoint the same way. The quick tunnel needs no account and takes two minutes; a domain or a tailnet funnel are the other two ways.
Always-on CPU
The Slack inbox dispatcher, the routine scheduler, the ingest loop and Drive sync run between requests. A platform that freezes an idle container acks Slack's event and then never does the work — which looks like the bot ignoring people. Pin a warm instance.
MASTER_KEY, backed up somewhere else
It seals every stored credential. Lose it and every workspace and every connection has to be reconnected by hand — by design, there is no recovery. bootstrap.sh generates one and then tells you to back it up; that instruction is the important half.
And from you
Hosted vs self-hosted
Same code. The difference is whose bill it lands on.
The hosted service runs the repository as it is. Nothing you learn on one applies any less to the other.
Start hosted and move on-premise later, or the other way round: the Slack app, the connections and the documents are each a few minutes to set up again, and nothing else has to move. Would rather we did the installing? We deploy it on your premises and hand you the keys — talk to us, or see pricing.
Clone it, or sign up. Same binary either way.
The repository is the product. Issues and pull requests are welcome, and the hosted service is there for the day you would rather not run it.
MIT-licensed. What setup involves