Orchestration
Quick links: Public API • Functions API
Overview
Orchestration fans out a post submission to provider‑specific publish functions using Cloud Tasks. Two orchestrators exist: publish (multi‑target) and repost (opt‑in destinations). Media may be normalized once (e.g., video rehost) before fan‑out.
Publish Orchestrator
Entry: apps/functions/orchestrators/publish-orchestrator.js
Core logic: apps/functions/orchestrators/publishing-service.js
- Input:
{ userId, postSubmissionId, request }(from Public API/v1/posts) - Target resolution: from
request.post.content.platformnormalized to lowercase;twitter→x.
Account Resolution
Multi-account support is built into the orchestration layer:
- If user has multiple accounts for a platform,
accountIdis required in the request - If user has exactly one account, it's automatically selected
- If no connected accounts exist, orchestration fails immediately with
missing_integrationserror - Account metadata is validated before task dispatch:
integrations/{platform}/{accountId}must existaccessTokenand required credentials must be present- Account must not be in error state
Media Normalization
Before platform fan-out, media may be normalized to meet platform requirements:
- Video rehosting: Videos are rehosted to Cloud Storage to ensure all platforms can access the media URL
- Original video is fetched and streamed to a new Cloud Storage object
- New public URL is generated and used across all platforms
- Prevents issues with platform-specific download restrictions
- Media validation: File types and sizes are checked against platform limits
- URL accessibility: All media URLs are validated for public accessibility before dispatch
Task Dispatch
For each platform, enqueues a Cloud Task to the provider function:
- facebook →
publishFacebook - instagram →
publishInstagram - threads →
publishThreads - x →
publishX - tiktok →
publishTiktok - youtube →
publishYoutube - linkedin →
publishLinkedin
Each task includes:
userId,postSubmissionId- Resolved
accountIdfor the platform - Normalized media URLs
- Platform-specific content (text, hashtags, etc.)
- Distributed trace context (
traceId,spanId)
State Management
Writes lifecycle to Firestore:
postSubmissions/{id}:status: 'orchestrating' → 'dispatched'targetsarray with resolved platforms and accountIdstraceIdfor distributed tracing
postSubmissions/{id}/runs/{platform}_{accountId}:status: 'queued'taskName(Cloud Tasks task identifier)accountId(resolved account)spanId(for distributed tracing)- Timestamps:
createdAt,queuedAt
Distributed Tracing
Every post submission generates a unique traceId that flows through all operations:
- Created at API entry point
- Passed to orchestrator
- Propagated to all platform publish tasks
- Logged in all structured log entries
- Enables end-to-end request tracing across async boundaries
Provider Publish Functions
Each provider validates connectivity under users/{uid}/integrations/{provider}, transitions run state to processing, performs the API call, then writes succeeded or failed with results/errors. On success, a user-visible post record is created under users/{uid}/posts.
References:
- Facebook:
integrations/facebook/{auth.js,post.js}— Pages feed posting. - Instagram:
integrations/instagram/{auth.js,post.js}— container + publish for images. - Threads:
integrations/threads/{auth.js,post.js}— text posts. - X:
integrations/x/{auth.js,post.js}— text tweets with PKCE + refresh. - TikTok:
integrations/tiktok/{auth.js,post.js}— OAuth, direct publish for video/photo with status polling. - YouTube:
integrations/youtube/{auth.js,post.js}— OAuth + refresh sweep; publish implemented. - LinkedIn:
integrations/linkedin/{auth.js,post.js}— UGC Posts API for text posting.
Repost Orchestrator
Entry: apps/functions/repost-orchestrator.js
- Triggered by two paths:
- Firestore:
onOrganicPostCreatedwatchesusers/{uid}/organicPosts/*for TikTok sources and enqueues repost orchestrator - HTTP:
pollerTiktokdetects newest TikTok video and createsorganicPostsdocs
- Firestore:
- Reads
users/{uid}/integrations/{platform}.enabled_for_repostand builds target list - Enqueues per‑platform repost jobs (via publish mapping) through the same publish fan‑out
Cloud Tasks
Helpers:
- Helper:
apps/functions/tasks.js— builds function URLs and enqueues tasks.
Queues:
orchestrate-posts,publish-*,repost-*. Names are configured via env in shared config.
Queue setup
Cloud Tasks queues are regional and must exist before tasks can be enqueued. Create them in the same region as your Functions (default: us-central1). The names are platform‑agnostic — create only the ones you use today, and add new queues as you onboard more providers.
Basic creation:
REGION=us-central1
# Orchestrator (dispatches per-target publish tasks)
gcloud tasks queues create orchestrate-posts --location="$REGION"
# Provider publish queues (create the ones you need)
gcloud tasks queues create publish-facebook --location="$REGION"
gcloud tasks queues create publish-instagram --location="$REGION"
gcloud tasks queues create publish-threads --location="$REGION"
gcloud tasks queues create publish-x --location="$REGION"
gcloud tasks queues create publish-tiktok --location="$REGION"
gcloud tasks queues create publish-youtube --location="$REGION"
# Example for a new platform (e.g., LinkedIn)
gcloud tasks queues create publish-linkedin --location="$REGION"
# Optional repost queues (only if you enable repost flows)
gcloud tasks queues create repost-instagram --location="$REGION"
gcloud tasks queues create repost-youtube --location="$REGION"
Recommended tuning (optional):
# Example conservative defaults; adjust to your provider rate limits
gcloud tasks queues update publish-x \
--location="$REGION" \
--max-dispatches-per-second=10 \
--max-concurrent-dispatches=5 \
--max-attempts=5 \
--min-backoff=10s \
--max-backoff=600s
Verification:
gcloud tasks queues describe orchestrate-posts --location="$REGION"
gcloud tasks queues describe publish-x --location="$REGION"
Notes:
- Keep
--locationaligned withFUNCTION_REGION/deployment region. Queues are regional. - Our enqueue helper posts unauthenticated HTTP requests to Cloud Functions URLs. If you restrict invocation, extend the task to include an OIDC token bound to a service account with
Cloud Functions Invokerand update the function to require auth. - Queue names are platform‑agnostic. For any new provider, create a
publish-<provider>queue and wire it in the orchestrator.
Observability
State Tracking
- Firestore serves as the source of truth for submission and per-platform run states
users/{uid}/apiRequestslogs Public API v1 calls withdurationMsand inferred targetspostSubmissions/{id}tracks orchestration status and target platformspostSubmissions/{id}/runs/{platform}_{accountId}tracks individual platform execution
Distributed Tracing
The system implements distributed tracing across all async operations:
- Trace context: Every request generates a
traceId(UUID) at the API layer - Span hierarchy: Each operation (orchestration, platform publish) creates child spans with
spanId - Depth tracking:
depthfield tracks nesting level (0 = root, 1 = orchestrator, 2 = platform) - Flow correlation:
flowId(typicallypostSubmissionId) groups related operations - Structured logs: All log entries include trace context for correlation
Query patterns for debugging:
// Find all logs for a specific post submission
db.collection('logs')
.where('flowId', '==', postSubmissionId)
.orderBy('timestamp')
// Find failed operations in a trace
db.collection('logs')
.where('traceId', '==', traceId)
.where('spanStatus', '==', 'error')
// Time-series queries with bucketHour
db.collection('logs')
.where('bucketHour', '>=', '2025-01-15-10')
.where('bucketHour', '<=', '2025-01-15-12')
.orderBy('bucketHour')
.orderBy('timestamp')
See also: Tracing & Logging