Register apps and enable signup
Start with GitHub sign-in, then add the chat apps your team uses. GitHub sign-in is implemented and needs only profile and verified-email access. You do not need to register all five supported sign-in providers.
Hosted rollout status, 7 September 2026: an OAuth identity-provider application has not yet been registered for the current rollout. Provider registration, server credential configuration, and a real first signup remain pending. A deployed dashboard or a successful health check does not complete those steps. For another community deployment, use the checklist below to establish its own readiness.
What you register, and who owns it
| Registration | Owner | Where its credentials go |
|---|---|---|
| Sign-in OAuth application | Async deployment operator | Server auth configuration and secret environment |
| Slack app installation or Discord bot | Tenant administrator | That Async tenant's Connections page |
| Gauzy Ever Async integration | Gauzy organization administrator | An Ever Gauzy connection in the chosen Async tenant |
The sign-in app is shared by people using that deployment. It establishes identity; it does not install a bot or grant task access. Tenants can save multiple Slack connections, multiple Discord connections, and Gauzy pairings at the same time. A person signing in with GitHub can connect either chat platform. Tenant administrators do not need the operator's OAuth client secret.
1. Choose the environment
Use separate sign-in applications and secrets for dev, stage, and production. Keep their chat apps and test workspaces separate too: a provider application's single events or interactions destination must not bounce between environments.
| Environment | Dashboard / OAuth base URL | Gauzy API for the Ever-hosted pairing |
|---|---|---|
| Dev | https://app-async-dev.ever.co | https://apidemo.gauzy.co |
| Stage | https://app-async-stage.ever.co | https://apistage.gauzy.co |
| Production | https://app-async.ever.co | https://api.gauzy.co |
For self-hosting, substitute your own public dashboard/API origin. The same Async server serves the dashboard, auth endpoints, and ingress. The marketing site and documentation host are not OAuth callback hosts.
The GitHub callback URLs to register are:
Dev: https://app-async-dev.ever.co/api/v1/auth/oauth/github/callback
Stage: https://app-async-stage.ever.co/api/v1/auth/oauth/github/callback
Prod: https://app-async.ever.co/api/v1/auth/oauth/github/callback
Local: http://localhost:8100/api/v1/auth/oauth/github/callback
The general rule is:
{auth.providers.oauth.base_url}/api/v1/auth/oauth/{provider}/callback
Use github, google, slack, discord, or gauzy for {provider}. Register
the exact scheme, hostname, port, and path, with no trailing slash. Slack
requires an HTTPS callback, so local Slack sign-in needs a public HTTPS tunnel.
There is no tenant ID or connection ID in a sign-in callback.
2. Register GitHub sign-in
- Open GitHub Developer settings, select OAuth Apps, and choose New OAuth App. An organization administrator can register the app under the organization that operates the service.
- Set Application name, for example
Ever Async Community (dev). - Set Homepage URL to the dashboard origin for that environment.
- Set Authorization callback URL to its exact GitHub callback from step 1.
- Register the application. Device Flow is not used by this browser flow.
- Copy its Client ID. Generate a client secret and store it directly in
the server's secret environment as
EVERASYNC_GITHUB_CLIENT_SECRET.
These fields follow GitHub's registration guide. GitHub supports multiple callback URLs, but separate applications keep environment credentials independently revocable.
Async requests read:user user:email in the authorization URL. No repository,
organization administration, or webhook scope is required for sign-in. The
account must have a primary email address that GitHub marks verified. See
GitHub's scope reference and email settings.
3. Configure the Async server
Merge this into the deployment's existing TOML configuration. Keep its storage, server, and other settings. Replace the example Client ID and choose the correct environment origin:
[auth]
mode = "authenticated"
provider = "oauth"
[auth.providers.oauth]
base_url = "https://app-async-dev.ever.co"
enabled = ["github"]
allowed_domains = ["your-community.example"]
[auth.providers.oauth.github]
client_id = "REPLACE_WITH_REGISTERED_CLIENT_ID"
client_secret_env = "EVERASYNC_GITHUB_CLIENT_SECRET"
allowed_domains is an email-domain allowlist, not an invitation list or a
GitHub organization check. Replace the example with a domain whose verified
addresses should sign in. Use allowed_domains = [] only when this deployment
intentionally accepts public signup from any verified account at its enabled
providers. Matching email domains do not automatically merge people into a
shared tenant.
Provision these server-side values through the deployment's secret manager and environment injection:
| Variable | Value |
|---|---|
EVERASYNC_GITHUB_CLIENT_SECRET | Secret from the registered OAuth application |
EVERASYNC_CONNECTION_ENCRYPTION_KEY | A securely generated, base64-encoded 32-byte key |
EVERASYNC_GAUZY_ALLOWED_ORIGINS | Comma-separated Gauzy API origins approved for this deployment |
The encryption key enables tenant connection management. Keep it stable across restarts and identical across replicas of the same environment. Back it up with the recovery material for that environment's persistent database; losing it prevents decryption of saved connection credentials. Replacing it is not a credential rotation procedure. Use separate keys between environments.
Set the Gauzy origin list explicitly, for example
https://apidemo.gauzy.co in dev. Entries are origins, without /api or another
path. For self-hosting, the operator must approve the tenant's actual Gauzy
origin before credentials can be sent there. This approval does not grant
access to any Gauzy tenant; its integration credentials still have to verify.
Never put secrets in browser build variables, source control, screenshots, or support tickets. The TOML names a secret environment variable; it does not contain the secret value. Restart or roll out the server after changing its operator configuration. Saving tenant connections later does not need a server restart.
Current authentication limits
The server selects one [auth].provider: oauth, local, or token.
Configuring an additional token-provider section does not make service tokens
work alongside OAuth for account or tenant APIs. Use public /healthz for
liveness; /api/v1/status and other protected diagnostics use the active
authentication provider.
An operator can separately opt in to a metrics-only bearer credential for
a monitoring scraper. Add this key to the existing [auth] table:
metrics_tokens_env = "EVERASYNC_METRICS_TOKENS"
Set that server-side environment variable to scraper:viewer:<secret>, replacing
<secret> with a securely generated secret. The format is id:role:secret;
the credential sent by the scraper is only the secret portion, as an
Authorization: Bearer header. This opt-in requires authenticated mode and
the auth-token build feature. A missing or malformed configured variable
fails startup.
This additional credential is accepted only for GET or HEAD on the exact
/metrics path. It cannot sign in, access tenant APIs, or authenticate through
a cookie or query parameter. Rotate it by updating the secret environment and
restarting the server; revoke it by removing the configuration key and secret
and restarting. Existing OAuth sessions and metrics credentials have separate
revocation paths, although a server restart also ends its in-memory sessions.
OAuth pending login state and sessions currently live in memory in each server process. A multi-replica deployment needs session affinity for the login start, callback, and subsequent authenticated requests. A process restart invalidates its sessions; shared database storage alone does not share them. Verify this behavior on the actual ingress before activating public signup. The existing security guide explains the session-storage limitation.
The current Ever-hosted app uses a single replica with SQLite and a Recreate deployment strategy. Restarting it logs users out; the static marketing and documentation sites' replica counts do not change this session behavior.
4. Complete the first signup
-
Check the selected environment without submitting credentials:
curl --fail https://app-async-dev.ever.co/healthz
curl --fail https://app-async-dev.ever.co/api/v1/auth/mode -
Confirm
modeisauthenticated,providerisoauth, andsso_providersincludesgithub. For tenant setup,supports_tenant_creationandconnections_enabledshould betrue. An empty provider list means signup has not been enabled. -
Open the dashboard and choose Continue with GitHub. Confirm the consent screen names the application you registered and requests only the expected identity scopes. Complete consent using a verified, allowed email address.
-
The first new identity without an existing membership or workspace binding receives its own empty tenant and the board role there. This is tenant ownership, not deployment-wide administrator access. Existing memberships are reused; a first sign-in from an already bound Slack/Gauzy workspace joins that tenant as a viewer unless membership grants another role.
-
Open Connections. Use the tenant menu to Create tenant for another independent team or switch among existing memberships. Creating a tenant makes the caller its board member and switches the session into it.
There is no OAuth password to create in Async. A GitHub sign-in does not grant access to another person's tenant simply because both use GitHub or share an email domain. The dashboard has no automatic bot-install OAuth flow: obtain the chat credentials below, then save them in the selected tenant.
5. Add a tenant's Slack workspace
-
A workspace administrator creates an app at Slack Apps using Create New App → From scratch, choosing that workspace.
-
Under OAuth & Permissions → Bot Token Scopes, add
chat:write,commands, andchannels:historyfor public-channel messages and/async. Addgroups:historyif private-channel messages are required, andim:writeif the bot will send direct-message digests. These are bot permissions;openid,email, andprofilebelong to the separate sign-in flow. -
Install the app into the workspace. Copy its Bot User OAuth Token (
xoxb-...). Under Basic Information → App Credentials, obtain the Signing Secret. A Client Secret is not a Signing Secret. -
In Async, select the tenant, open Connections → Add Slack, enter a connection name and those two credentials, then Save connection. Saving verifies the workspace identity and leaves the connection disabled.
-
Copy the saved card's URLs into the Slack application:
Async field Slack setting Events URL Event Subscriptions → Request URL Commands URL Slash Commands → Create New Command, named /asyncInteractivity URL Interactivity & Shortcuts → Request URL The managed paths contain the saved connection ID:
/ingress/connections/<connection-id>/events
/ingress/connections/<connection-id>/commands
/ingress/connections/<connection-id>/interactions -
Enable Event Subscriptions and subscribe to
message.channels; addmessage.groupsfor private channels. For the optional invite-time charter, also addmember_joined_channeland the correspondingchannels:readorgroups:readscope. Reinstall the app after changing scopes. -
Enable interactivity, invite the bot to the intended channels, then choose Enable on its Async card. Try
/async statusin a test channel.
Slack documents message event permissions, private-channel events, message posting, opening DMs, and membership events. Async uses HTTP events and interactions; Socket Mode is not this setup path.
Repeat for additional workspaces. Each installation has its own bot token. For one Slack application installed across workspaces, configure one saved connection's callback URLs on that application. Signed callbacks are routed to its matching enabled workspace connection, including connections in other Async tenants when their verified bindings identify them. Sharing a callback does not share tenant data or bot tokens. Cross-workspace Slack distribution and token issuance remain the app owner's responsibility; Async's sign-in callback does not exchange Slack bot-install authorization codes.
6. Add a tenant's Discord server
-
Open the Discord Developer Portal and create an application. Copy its Application ID and Public Key from General Information. On the Bot page, create or reset the bot token and store it privately.
-
Configure Guild Install on the Installation page. Include the
botandapplications.commandsscopes. Give the bot View Channels, Send Messages, Send Messages in Threads, and Read Message History permissions. Install it in the intended server using the application link; the installing member needs permission to manage that server. Follow Discord's installation guide. -
For ordinary channel messages, enable Message Content Intent under Bot → Privileged Gateway Intents. Verified apps, required at 100 or more servers, also need Discord's approval for privileged intents. Do not enable unrelated privileged intents. See the Gateway intent reference.
-
Obtain the server's guild ID, then select Connections → Add Discord in the correct Async tenant. Enter its name, Discord server ID, 64-character Discord application public key, and Bot token. Save the connection.
-
Copy the card's Interactions URL into the application's Interactions Endpoint URL. Its managed path is:
/ingress/connections/<connection-id>/interactions -
Register
/async. Async handles the command but does not create its Discord registration. The following Node script registers one guild command using environment variables already set securely in your shell. It prints only the HTTP status. Run it for each intended server; it does not bulk-replace other commands. See Discord's command API.// register-async-command.mjs
const { DISCORD_APPLICATION_ID: app, DISCORD_GUILD_ID: guild,
DISCORD_BOT_TOKEN: token } = process.env;
if (!app || !guild || !token) throw new Error('Set the three environment values');
const response = await fetch(
`https://discord.com/api/v10/applications/${app}/guilds/${guild}/commands`,
{
method: 'POST',
headers: {
Authorization: `Bot ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'async', type: 1,
description: 'Control Ever Async in this conversation',
options: [{
name: 'args', type: 3, required: false,
description: 'status | on | off | sensitivity high | delay 150',
}],
}),
},
);
console.log(`Discord command registration: HTTP ${response.status}`);
if (!response.ok) process.exitCode = 1; -
Choose Enable in Async. Try
/asyncwithargsset tostatus. With Message Content Intent configured, enabling the connection starts its Gateway receiver automatically. Otherwise the card says Interactions only. After changing the intent in Discord, Edit → Save connection or Enable persists the newly verified setting; Verify connection alone does not change saved capabilities. -
Test a channel message and a private nudge. Recipients must allow the bot's DMs. Accepted suggestions appear as bot replies attributed to the author, in the original conversation; they do not replace the author's message.
One Discord application can serve several saved guild connections with one interactions URL. Async verifies the application and guild before routing; unmapped or ambiguous destinations are rejected. One saved connection is needed for each server. The receiver uses Discord's advertised shard count and routes only events for that saved server.
Shared callback URLs and disabling a connection
The connection ID in a shared application's URL identifies a saved record used to verify that application's request. Disabling that record stops its own workspace/server but keeps the shared URL usable for other enabled, verified connections of the same application. It is not an application-wide shutdown. Disable each destination connection to stop the whole application's Async traffic. Keep the saved record's signing/public-key details valid while its URL is registered with the provider.
7. Pair the tenant with a Gauzy organization
- In Gauzy, select the organization and open Integrations → Ever Async. The deployment must include that integration plugin and its UI. The user needs the integration permissions appropriate to creating or editing it.
- Select the projects Async may read. Map each person with all four fields:
chat provider (
slackordiscord), verified workspace/server ID from Async, chat user ID in that workspace, and employee ID in this Gauzy organization. - Save or connect the integration and retain the displayed integration ID, Gauzy tenant ID, organization ID, API URL, and one-time API key and secret. This is a read-only integration credential, not the Gauzy OAuth client secret and not the server's JWT signing secret.
- In the chosen Async tenant, open Connections → Pair Gauzy. Paste those details and the Gauzy web app URL. Select the verified Slack/Discord source connections that may use this pairing, then save and enable it.
- Test a mapped user's task reference from an allowed project. Confirm the resulting link opens the intended Gauzy deployment and project. Test an unselected source as well; it must not gain access through this pairing.
The explicit All verified connections in this tenant option also includes future verified chat connections in that tenant. Prefer Selected connections when the pairing should remain limited to specific workspaces. Several pairings can coexist. Credential rotation starts in Gauzy, followed by saving the new key and secret in the existing Async connection. See Manage connections and the Gauzy connector guide.
Other supported sign-in providers
These are alternatives or additions to the operator's GitHub sign-in. They are
not required to add the corresponding tenant chat connection. Add the chosen
provider ID to enabled and configure its subsection with client_id and
client_secret_env.
| Provider ID | Registration | Exact scopes requested by Async |
|---|---|---|
google | Google Auth Platform OAuth client, type Web application | openid email profile |
slack | Slack app with OAuth & Permissions → Redirect URLs | openid,email,profile |
discord | Discord application with OAuth2 → Redirects | identify email |
gauzy | Confidential OAuth client in the selected Gauzy deployment | profile email |
- Google: configure application branding, consent audience, and the Web
application client's authorized redirect URI. Register the exact environment
callback ending
/google/callback. Follow the audience's test-user and publication requirements before community signup. Store the client secret asEVERASYNC_GOOGLE_CLIENT_SECRET. Google's setup reference. - Slack: save an HTTPS redirect ending
/slack/callback. Configure the three OpenID user scopes and useEVERASYNC_SLACK_CLIENT_SECRET. Keep the OpenID sign-in authorization separate from the bot-install authorization; Slack does not allow those scope families in one request. The same Slack application may support both flows. Sign in with Slack. - Discord: save the redirect ending
/discord/callback, use the client ID and OAuth client secret inEVERASYNC_DISCORD_CLIENT_SECRET, and request only identity scopes for login. The bot token, application public key, and guild-install scopes from step 6 serve the chat connection instead. Discord OAuth2. - Gauzy: an authorized Gauzy administrator registers a confidential client
through
POST /api/oauth/clients, withredirectUriscontaining the exact/gauzy/callback,allowedScopes: ["profile", "email"],allowedGrantTypes: ["authorization_code"], andpkceRequired: false. Set[auth.providers.oauth.gauzy] base_urlto that Gauzy API origin andclient_secret_env = "EVERASYNC_GAUZY_CLIENT_SECRET". Follow the complete Gauzy OAuth registration. This registration is separate from each organization's read-only task pairing.
Only Gauzy's provider subsection accepts a custom base_url. For the other
providers, the shared OAuth base_url is the Async origin; their identity
endpoints are fixed. See Setting up SSO for per-provider
failure messages and detailed configuration.
Activation checklist
- The operator registered an identity-provider app for each environment being activated and stored its secret on the corresponding Async server.
- Each app's exact callback matches that environment's OAuth
base_url. No dev or stage login redirects into production. - Auth mode is
authenticated; the expected sign-in button is present. A real allowed account completes consent, receives its tenant, signs out, and signs in again. Disallowed-domain and declined-consent cases are checked. - Multi-replica routing keeps OAuth start, callback, and authenticated requests on the correct process; restart behavior is understood and tested.
- Persistent storage and its backup are working. All replicas in the same environment use the same protected connection-encryption key.
- Two different users/tenants cannot see or modify each other's saved connections or credentials. Tenant switching clears unsaved password fields.
- Chat apps are installed, required permissions/events/intents are enabled,
callback validation succeeds, and
/async statusworks in test conversations. - A real message, private nudge, author acceptance, and resulting bot reply are verified. Shared-app routing and disabling one destination are checked.
- Gauzy pairing verifies the intended deployment, tenant, and organization; selected projects and scoped employee mappings return only permitted context.
- Secret rotation and disabling a connection have been exercised without exposing credentials. Operators have recorded the supported rollback.
Until provider registration and the first real signup are complete, report signup as pending. Code, build, provider-handshake, and browser-fixture tests are useful checks, but none registers an application in the provider console.