pubsub transport
pulls inbound events from a Cloud Pub/Sub subscription, so your Comis daemon
reaches out to Google rather than the other way around. An opt-in webhook mode
is available when you would rather Google push events to a public HTTPS endpoint.
This page walks you through creating the Chat app, wiring the transport, and
configuring authentication.
You don’t need to understand the technical details to use this feature. The configuration examples below are copy-paste ready.
Prerequisites
- Comis installed and running (Quickstart)
- A Google Cloud project with the Google Chat API and the Cloud Pub/Sub API enabled
- A service account in that project, with its key JSON downloaded — this is the only credential Comis needs; there is no bot token
- For the default
pubsubtransport: a Pub/Sub topic and a pull subscription (see Setup) - For the opt-in
webhooktransport only: a public HTTPS URL that reaches your Comis gateway (see Public endpoint)
Public endpoint
Inwebhook mode, Google delivers each event as an authenticated HTTPS POST to
/channels/googlechat on your gateway, so the gateway must be reachable from the
internet at https://your-host/channels/googlechat. The route rides your
gateway’s existing host, port, and TLS surface (gateway.host / gateway.port)
and is mounted only when channels.googlechat.enabled is true and
mode is webhook; in pubsub mode, or while the channel is disabled, no
inbound route exists at all. Every inbound request is Bearer-JWT-verified before
it reaches your agent, so an unauthenticated POST is rejected regardless of how
you expose the endpoint.
You have several ways to make the gateway publicly reachable. They are listed
most-hardened first; all of them see the same inbound token verification, so
the choice is about network exposure, not authentication.
1
Reverse proxy with HTTPS (production)
Terminate TLS at a reverse proxy (Nginx, Caddy) in front of the gateway and
forward
/channels/googlechat to it. This gives you full control over
certificates, headers, and rate limiting. See
Reverse proxy for a worked Nginx and Caddy
configuration.2
Tailscale Funnel (production, no open port)
Tailscale Funnel publishes a local
port to the public internet over HTTPS with an auto-provisioned certificate
and a stable Your endpoint becomes
*.ts.net hostname — without forwarding a port on your router
or firewall.https://your-machine.your-tailnet.ts.net/channels/googlechat. Funnel
handles the certificate; Comis still verifies every inbound request.3
Dev tunnel (testing only)
For local testing, a tunnel such as ngrok gives you a
temporary public HTTPS URL that forwards to your gateway port. Use the tunnel
URL as the endpoint while you iterate; move to a reverse proxy or Funnel
before you run in production.
Setup
Google Chat needs a Google Cloud project with a service account (the app’s credential) and a Chat app configuration that selects a transport. The defaultpubsub transport also needs a Pub/Sub topic and a pull subscription.
1
Create a project and enable APIs
In the Google Cloud console, create (or
pick) a project and enable the Google Chat API and the Cloud Pub/Sub
API. Note the project’s number — you will need it for
webhook mode’s
project-number audience.2
Create a service account and download its key
Create a service account in the project and download its key JSON. This
single credential lets Comis mint the tokens it needs to call the Chat API;
there is no separate bot token. Keep the key file safe — it is a secret.
3
Configure the Chat app and choose a transport
On the Chat API Configuration page, set the app name and avatar, then
pick how inbound events are delivered:
- Cloud Pub/Sub (default)
- HTTPS endpoint (webhook)
- Create a Pub/Sub topic and a pull subscription on it
(
projects/{project}/subscriptions/{sub}). - Grant the Chat service account
(
chat@system.gserviceaccount.com) the Pub/Sub Publisher role on the topic, so Google can publish inbound events to it. - Grant your service account the
roles/pubsub.subscriberrole on the subscription, so Comis can pull events. - In the Chat app’s connection settings, select Cloud Pub/Sub and point it at your topic.
4
Configure Comis
Add the Google Chat channel to your Comis configuration file
(Set the service-account key in your
~/.comis/config.yaml). The default pubsub transport:~/.comis/.env file (the full key JSON
on one line):5
Restart and verify
Restart the Comis daemon to pick up the new configuration:Check that the Google Chat probes pass:Then message the bot from a Google Chat space or direct message and confirm
it replies.
Authentication
Google Chat authenticates with a service-account key — the same credential in both transports. The key is the only secret; keep it in~/.comis/.env as
GOOGLECHAT_SA_KEY or supply a SecretRef. What differs between the modes is how
inbound events are trusted.
- pubsub (default)
- webhook
Comis pulls events from your Pub/Sub subscription, so inbound trust comes
from the subscription’s IAM — there is no public endpoint and no inbound
token verification to configure.
Configuration
The full option table — all fields, types, and defaults — lives in the Configuration reference. The service-account key environment variable is documented in Environment variables. This section covers the one option that carries a security consequence worth spelling out.Sender allowlist and the email caveat
allowFrom restricts who can talk to the bot. When it is non-empty (and
allowMode is the default allowlist), only listed senders reach the agent.
Each entry is a Google Chat resource id — a users/{id} for one person or a
spaces/{id} for a whole space.
Media
Inbound attachments (images, documents, voice, video) are downloaded exclusively through the Chat API’sattachmentDataRef.resourceName via
media.download, host-pinned to chat.googleapis.com, and MIME-checked before
the media pipeline (transcription, vision, document extraction) sees them.
A file shared through the Drive picker arrives without a resourceName and
degrades with an honest WARN: downloading Drive-hosted content needs a user-OAuth
credential plus the Drive scope, which the service-account app does not have.
Google Chat media is inbound only. Uploading files or videos into a space is
a user-auth-only method and a later addition; today the agent replies with text
and Cards v2 messages.
Liveness
Inpubsub mode the pull loop self-heals: it retries with bounded, jittered
backoff and de-duplicates redelivered events, so a transient Pub/Sub error
recovers without operator action.
In webhook mode the channel is exempt from the health monitor’s stale-reap —
because Google pushes events over a connection Comis cannot watch, a mis-wired or
silently broken endpoint would otherwise report healthy forever, and the only
symptom would be a bot that quietly stops receiving messages. Two guards close
that gap:
comis doctorruns the Google Chat (googlechat-health) probes: the service-account key parses and resolves; the Pub/Sub subscription is reachable (inpubsubmode — a failure names theroles/pubsub.subscribergrant) or the ingress endpoint is reachable (inwebhookmode); an inbound event has arrived recently;allowFromentries are immutableusers/{id}ids rather than email-shaped; the webhookaudienceshape matchesaudienceType(a mismatch silently rejects every inbound request); andautoReplyEngine.groupActivation: "always"draws a warning because it is inert on this platform. The reachability and recent-inbound probes skip (rather than fail) when the daemon is down.- A missed-inbound alert compares the time since the last inbound event to
missedInboundThresholdMs(default 6 hours). On breach it emits achannel:inbound_silentevent and a WARN that surfaces as acomis fleethealth signal. Lower the threshold for a busy space where a few hours of silence is itself a red flag.
Live-smoke checklist
The build gates cover the adapter, mapper, renderer, auth, Pub/Sub source, and gateway ingress. The end-to-end sign-off needs a real Chat app and a Google Cloud project, so run this operator checklist once against a live account:- Text round-trips in a space and in a direct message
- A threaded reply lands on the original message’s thread; the bot edits and deletes its own messages
- A Cards v2 card renders, and clicking Approve on an approval card is authorized to the clicking user — a non-allowlisted user cannot approve
- An inbound image and an inbound document each resolve through the media pipeline
- Both
pubsubandwebhookmodes start cleanly -
comis doctorshows the Google Chat (googlechat-health) probes green; in webhook mode the missed-inbound alert fires after the configured silence window
Local emulator (self-drive testing)
The live-smoke checklist above needs a real Chat app. For an offline, no-Google round trip — and for the self-driving live-test rig — an in-tree emulator plays the Google side (a fake Chat REST API + Pub/Sub pull endpoint + a JWKS signer for webhook tokens). It lives attest/live/emulators/googlechat/ and is driven from
test/live/self-driving/.
Google Chat has both inbound and outbound loopback bridges, and all are
off-by-default environment variables set only on the test daemon — never in
production:
Wiring (as the operator, on the test box):
test/live/scenarios/channels/googlechat-emulator.test.ts, which
pushes a signed event through the real ingress and adapter into the emulator’s
oracle.
What your agent can do
Once connected to Google Chat, your agent can:- Send, edit, and delete messages in spaces and direct messages
- Reply in a space thread (threaded on the original message)
- Send Cards v2 messages with interactive buttons, including default-deny approval cards for privileged actions
- Receive inbound images, documents, and other attachments (routed through the media pipeline)
- Detect when it is
@mentionedin a space
Platform limits
Troubleshooting
Bot can send messages but never receives any (pubsub mode)
Bot can send messages but never receives any (pubsub mode)
What happened: Comis cannot pull from the Pub/Sub subscription, or Google
is not publishing to the topic.How to fix it: Grant your service account the
roles/pubsub.subscriber
role on the subscription, and confirm subscriptionName is the full
projects/{project}/subscriptions/{sub} path. Verify the Chat app’s Pub/Sub
connection publishes to the topic that subscription is attached to, and that
the Chat service account has the Publisher role on the topic.Webhook inbound requests are rejected with a 401
Webhook inbound requests are rejected with a 401
What happened: Bearer-JWT verification failed — usually an
audienceType
/ audience mismatch.How to fix it: Match audienceType to how Google mints the token:
project-number (aud is your project number, issuer
chat@system.gserviceaccount.com) or app-url (aud is your endpoint URL).
Set audience to the matching value, and confirm gateway.enabled is true
and the public URL resolves to /channels/googlechat.Service-account key fails to parse at startup
Service-account key fails to parse at startup
What happened: The
serviceAccountKey value is not the full
service-account key JSON.How to fix it: Supply the complete key JSON (it must contain the
client_email and the private-key material), not a file path or a partial
copy. If you use a ${GOOGLECHAT_SA_KEY} reference, confirm the variable is
set in ~/.comis/.env. Comis never prints the key — the doctor probe reports
only whether it parsed.Later additions
The Google Chat channel focuses on chat, Cards v2, threaded replies, edit/delete, and inbound media. These are documented as not-yet-shipped so you can plan around them:- Uploading files and videos into a space (needs a user-OAuth credential — the service-account app cannot upload)
- Sending and receiving reactions (user-auth-only on this platform)
- Fetching un-approved message history (needs admin approval and the read-only scope)
- A Google Workspace Events event source (the path to inbound reactions and listening to every space message)
- Typing indicators and token-by-token streaming
All Channels
Compare all 11 supported platforms side by side.
Configuration reference
Every
channels.googlechat option, type, and default.Environment variables
The
GOOGLECHAT_SA_KEY secret and the Google Chat live-test seams.Secret Management
Learn how to manage API keys and tokens securely.
