This document specifies the technical architecture standard for Hybrid Identity & Organization Synchronization, API-First Delegation (Method 3) for Organization Management, the Centralized Generic OAuth 2.0 PKCE Bridge (api.dos.me), and Autonomous Agent Connections (DOSClaw) between DOS.Me ID and all member applications across the Crove Ecosystem (Crove Post, Crove CRM, Crove Sign, Crove Cal, and Crove Desk).
The Crove ecosystem connects independent, best-in-class open-source core platforms through a unified identity layer, shared multi-tenant database infrastructure, and event-driven data synchronization:
┌───────────────────────────────┐
│ DOS.Me ID & Platform │
│ (Supabase Auth + api.dos.me) │
└───────────────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ Crove Post │ │ Crove CRM │ │ Crove Sign │
│ (Postiz - NestJS) │ │ (Twenty - TypeORM)│ │(Documenso - Next) │
│ Schema: post │ │ Schema: core │ │ Schema: sign │
└───────────────────┘ └───────────────────┘ └───────────────────┘
│ │ │
└────────────────────────────────┼────────────────────────────────┘
▼
┌───────────────────┐
│ Crove Cal │
│ (Cal.com - TRPC) │
│ Schema: cal │
└───────────────────┘
- Each member application (Postiz, Twenty CRM, Documenso, Cal.com) maintains its own database schema for maximum operational autonomy and smooth upstream synchronization.
- Push Webhooks Only: If a user creates an Organization on DOS.Me before ever logging into a satellite app, the satellite app lacks the local
User IDrequired to establish foreign key constraints. - JIT Login Sync Only: If a user is added to a new Organization on DOS.Me while already authenticated, satellite apps will suffer data drift unless the user explicitly logs out and logs back in.
👉 Standard Architecture: Two-Phase Hybrid Sync (JIT + Webhooks).
When a user signs in via DOS ID:
-
User & Profile Synchronization: The satellite app consumes the
id_token/userinfoclaims from DOS.Me:-
sub: Unique ID from DOS.Me / Supabase Auth -
email: Primary email address -
name: Full display name -
picture: Avatar URL$\rightarrow$ The satellite app automatically provisions or updates its localUserrecord, syncing name and profile picture.
-
-
Organization Provisioning:
- If the user has no existing Organization in the satellite app, the app inspects the
organizations: [{ id, name, slug, role }]claim to provision the corresponding Workspace/Organization and assign appropriate roles (SUPERADMIN/ADMIN/USER).
- If the user has no existing Organization in the satellite app, the app inspects the
When an administrative change occurs on DOS.Me (organization renamed, member invited, member removed, subscription tier updated):
- DOS.Me Event Router dispatches an HMAC-SHA256 signed HTTP POST webhook (
X-DOS-Signature: sha256=...) to internal endpoints across satellite apps:https://post.crove.com/api/webhooks/dos-org-synchttps://crm.crove.com/api/webhooks/dos-org-synchttps://sign.crove.com/api/webhooks/dos-org-synchttps://cal.crove.com/api/webhooks/dos-org-synchttps://desk.crove.com/api/webhooks/dos-org-sync
To ensure seamless user experience (staying within the app) and absolute data consistency (Single Source of Truth), satellite apps do not write directly to public.organizations. Instead, they delegate creation via DOS.Me Central API Hub (api.dos.me):
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────────┐
│ User creates │──────▶│ Satellite App │──────▶│ api.dos.me │──────▶│ Database (Supabase) │
│ Org in app UI │ │(Post/Sign/CRM) │ │(NestJS Org Svc) │ │ schema: public │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └──────────────────────┘
│
┌──────────────────┴──────────────────┐
│ Webhook Dispatcher (HMAC-SHA256) │
├─────────────────────────────────────┤
│ ▶ Crove Post (`/api/webhooks/...`) │
│ ▶ Crove CRM │
│ ▶ Crove Sign │
│ ▶ Crove Cal │
│ ▶ Crove Desk │
└─────────────────────────────────────┘
- Satellite App Frontend / Server Action:
- Endpoint:
POST https://api.dos.me/organizations(orhttps://beta-api.dos.me/organizations) - Header:
Authorization: Bearer <user_access_token> - Body:
{ "name": "Acme Corporation", "slug": "acme-corp" }
- Endpoint:
- Processing at
api.dos.me:- Validates Quota / Plan Entitlements (ensuring Free/Pro/Enterprise limits are enforced).
- Inserts organization into
public.organizationsand assigns the user asOWNERinpublic.org_members. WebhookDispatcherServicefans out anorg.createdevent to all registered satellite endpoints.- Returns
{ success: true, organization: { id, name, slug } }.
- Satellite App Reaction: Sets
active_orgto the newly created Organization ID without requiring page reloads or external redirects.
In the Crove ecosystem, satellite applications differ in their OAuth client capabilities:
- Crove Post (Postiz): Uses a generic OAuth 2.0 client without native PKCE support (
code_challenge/code_verifier). Because Supabase Auth enforces OAuth 2.1 PKCE, Postiz routes through the Centralized PKCE Bridge onapi.dos.me. - Crove CRM (Twenty), Crove Sign (Documenso), Crove Cal (Cal.com), Crove Desk: Support native standard OpenID Connect (OIDC) with PKCE out-of-the-box via NextAuth/Passport and can authenticate directly against Supabase Auth.
Previously, Cloudflare Workers were used as stateful intermediaries. All PKCE Bridge functionality for Crove Post is now centralized inside apps/api (api.dos.me and beta-api.dos.me), eliminating external Cloudflare Workers.
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ Crove Post │ │ api.dos.me │ │ Supabase Auth │
│ (Postiz - NestJS) │ │ (Generic OAuth Bridge) │ │ (OAuth 2.1 Server PKCE) │
└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘
│ │ │
│ 1. GET /oauth/authorize (No PKCE) │ │
│───────────────────────────────────▶│ 2. Generate S256 PKCE Pair │
│ │ (verifier & challenge) │
│ │ 3. Redirect to Supabase │
│ │───────────────────────────────────▶│
│ │ │
│ │ 4. Supabase returns auth code │
│ │◀───────────────────────────────────│
│ 5. Redirect with Bridge Code │ │
│◀───────────────────────────────────│ │
│ │ │
│ 6. POST /oauth/token (Basic/Post) │ │
│───────────────────────────────────▶│ 7. Exchange code + verifier │
│ │───────────────────────────────────▶│
│ │ 8. Return Token Pair │
│ │◀───────────────────────────────────│
│ 9. Return access_token + claims │ │
│◀───────────────────────────────────│ │
│ │ │
│ 10. GET /oauth/userinfo (Bearer) │ │
│───────────────────────────────────▶│ 11. Enrich `organizations` array │
│ │ from PostgreSQL DB │
│ 12. Return UserInfo + Orgs │ │
│◀───────────────────────────────────│ │
# ================================================================
# CROVE POST (POSTIZ) - CENTRALIZED GENERIC OAUTH 2.0 CONFIG
# ================================================================
POSTIZ_GENERIC_OAUTH=true
# --- PRODUCTION ENVIRONMENT ---
POSTIZ_OAUTH_URL=https://api.dos.me
POSTIZ_OAUTH_AUTH_URL=https://api.dos.me/sso/authorize
POSTIZ_OAUTH_TOKEN_URL=https://api.dos.me/sso/token
POSTIZ_OAUTH_USERINFO_URL=https://api.dos.me/sso/userinfo
# --- BETA ENVIRONMENT ---
# POSTIZ_OAUTH_URL=https://beta-api.dos.me
# POSTIZ_OAUTH_AUTH_URL=https://beta-api.dos.me/sso/authorize
# POSTIZ_OAUTH_TOKEN_URL=https://beta-api.dos.me/sso/token
# POSTIZ_OAUTH_USERINFO_URL=https://beta-api.dos.me/sso/userinfo
POSTIZ_OAUTH_CLIENT_ID=crove-postiz
POSTIZ_OAUTH_CLIENT_SECRET=<CROVE_POSTIZ_OAUTH_CLIENT_SECRET>
POSTIZ_OAUTH_SCOPE="openid profile email organizations offline_access"
NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME="DOS ID"
NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL="/icons/generic-oauth.svg"# Standardized naming across other Crove OS apps connecting via Supabase OAuth 2.1:
CROVE_OAUTH_CLIENT_ID=18790ccb-4d71-48cd-ad24-aee5f3ced3da
CROVE_OAUTH_CLIENT_SECRET=<CROVE_OAUTH_CLIENT_SECRET>
# Token Auth Method: client_secret_basic (or client_secret_post)DOSClaw AI agents connect via Option A (One-Touch OAuth 2.0):
- Client IDs:
- Beta:
pca_dosclaw_beta_7ef5e5f1 - Production:
pca_dosclaw_prod_18790ccb
- Beta:
- Redirect URIs:
- Beta:
https://beta-api.dos.me/oauth/crove-post/callback - Production:
https://api.dos.me/oauth/crove-post/callback
- Beta:
- Token Format: Crove Post issues scoped
pos_*tokens upon consent. - Token Revocation (RFC 7009):
POST /oauth/revokewith{ token: "pos_..." }immediately invalidates access tokens when agents disconnect. - Security Boundary: The token is stored securely in DOS-Me Vault. DOS.AI agents only receive an
agent_connection_bindingreference and never see plaintext credentials.
POST /api/webhooks/dos-org-sync HTTP/1.1
Host: post.crove.com
Content-Type: application/json
X-DOS-Signature: sha256=<hex_hmac_sha256_signature>{
"event": "org.member_added",
"timestamp": "2026-08-23T08:00:00Z",
"data": {
"org_id": "org_dos_123456",
"org_name": "Tingee Corporation",
"user_id": "usr_dos_789012",
"user_email": "member@crove.com",
"user_name": "Nguyen Van A",
"role": "ADMIN"
}
}| Event | Description | Crove Post Handler |
|---|---|---|
org.created |
New organization created | Creates Organization & sets user as SUPERADMIN |
org.updated |
Organization name changed | Updates Organization.name |
org.deleted |
Organization removed | Sets Organization.deletedAt |
org.member_added |
Member added to organization | Creates UserOrganization record with mapped role |
org.member_removed |
Member removed from organization | Removes UserOrganization association |
DOS.Me (public) |
Crove Post (post) |
Crove CRM (core) |
Crove Sign (sign) |
Crove Cal (cal) |
|---|---|---|---|---|
profiles.user_id |
User.providerId |
user.id / sub |
User.id |
users.id |
profiles.email |
User.email |
user.email |
User.email |
users.email |
profiles.name |
User.name |
user.name |
User.name |
users.name |
organizations.id |
Organization.id |
workspace.id |
Organisation.id |
Team.id |
organizations.name |
Organization.name |
workspace.name |
Organisation.name |
Team.name |
org_members.role |
UserOrganization.role |
workspaceMember.role |
OrganisationMember.role |
Membership.role |
The Crove OS ecosystem adopts a strict 2-Tier Separation of Concerns between high-performance local database mirrors and deep agentic tool calling:
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ CROVE OS 2-TIER HYBRID ARCHITECTURE │
├──────────────────────────────────────────┬──────────────────────────────────────────────┤
│ TIER 1: Identity & Entity Data Sync │ TIER 2: Deep Agentic Business Actions │
│ (Companies, Customers, Organizations) │ (Create Deals, Renewals, Assign Tasks) │
├──────────────────────────────────────────┼──────────────────────────────────────────────┤
│ DATABASE SYNCHRONIZATION │ MCP PROTOCOL │
│ (PostgreSQL Mirror / < 5ms Latency) │ (Model Context Protocol Tool Calling) │
│ │ │ │ │
│ • Master SSOT: DOS.Me & Twenty CRM │ • twenty_crm.create_opportunity(...) │
│ • Local Mirrors: post.* / desk.* │ • twenty_crm.get_subscription_status(...) │
│ • Bi-directional Webhook Dispatch │ • twenty_crm.create_task(...) │
│ • JIT (Just-In-Time) Sync on Login │ • crove_sign.get_contracts(...) │
│ • Event Ingress: api.dos.me/internal/ │ • Deep business validation & side-effects │
│ events/publish │ │
└──────────────────────────────────────────┴──────────────────────────────────────────────┘
All satellite applications verify inbound events using constant-time HMAC-SHA256 comparison:
import * as crypto from 'crypto';
export function verifyEcosystemWebhook(
rawBody: string | Buffer,
signatureHeader: string,
secret: string
): boolean {
if (!signatureHeader || !secret) return false;
const cleanSignature = signatureHeader.replace(/^sha256=/, '').trim();
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
if (cleanSignature.length !== expected.length) return false;
return crypto.timingSafeEqual(
Buffer.from(cleanSignature, 'hex'),
Buffer.from(expected, 'hex')
);
}Crove Post exposes high-level MCP tool calling via dual-registration (aliasing), supporting upstream Postiz conventions as well as Crove OS ecosystem conventions (crove_post.* and post.*):
| Capability | Canonical / Modern Alias | Ecosystem Aliases | Upstream Tool Name |
|---|---|---|---|
| Schedule Post | schedule_post |
crove_post_schedule_post, post_schedule_post |
integrationSchedulePostTool |
| List Channels | list_channels |
crove_post_list_channels, post_list_channels |
integrationList |
| List Posts | list_posts |
crove_post_list_posts, post_list_posts, get_posts |
postsListTool |
| List Groups | list_groups |
crove_post_list_groups, post_list_groups, list_customers |
groupList |
| Update Post Settings | update_post_settings |
crove_post_update_post_settings, post_settings |
postSettingsTool |
| Trigger Channel Sync | trigger_integration |
crove_post_trigger_integration |
integrationTriggerTool |
| Validate Channel | validate_integration |
crove_post_validate_integration |
integrationValidationTool |
| Upload Media URL | upload_from_url |
crove_post_upload_from_url |
uploadFromUrlTool |
| Generate Image | generate_image |
crove_post_generate_image |
generateImageTool |
| Generate Video | generate_video |
crove_post_generate_video |
generateVideoTool |
| Video Options | generate_video_options |
crove_post_generate_video_options |
generateVideoOptions |
| Video Processing | video_function |
crove_post_video_function |
videoFunctionTool |
Agents connecting via MCP (e.g. DOSClaw, Cursor, Claude Desktop, ChatGPT) can invoke tools using either convention without breaking changes.