Viewport schedules videos to Instagram, YouTube and TikTok and reads back how they did. You run your own copy: your accounts, your database, your hosting.
About 30 minutes. Mostly creating free accounts and pasting keys between browser tabs.
| Service | What it does here |
|---|---|
| Supabase | Your queue, your metrics, your uploaded videos |
| Composio | The connection to Instagram and YouTube |
| Cloudflare | Serves the app, starts the publish job on time |
| GitHub | Runs the publishing and the metrics sync |
All four are free at the volume Viewport uses. You also need Node 22 or newer, your licence key, and an Instagram Business or Creator account.
Create a project. Any region, any name.
Create the tables. Open SQL Editor, paste all of keyup-scheduler-cron/supabase/schema.sql, run it.
Create the bucket. Storage → New bucket, named exactly keyup-videos, with Public: on.
Copy two values from Project Settings → API: the Project URL and the service_role key.
Create a project and copy its API key. That is all.
In keyup-scheduler-cron/, create .env:
SUPABASE_URL=https://yourproject.supabase.co
SUPABASE_SERVICE_KEY=your-service-role-key
COMPOSIO_API_KEY=your-composio-key
YOUTUBE_DEFAULT_PRIVACY=unlisted
Leave YouTube on unlisted until your first post has gone out correctly.
Check it:
npm install
npm run doctor
This asks every service whether it can do what Viewport will ask of it, and names the one that cannot. Do not continue until Supabase is clean.
Put the code in your own repository — fork it or push it to a new private one. The publish job runs there, on your Actions minutes.
Add these as secrets under Settings → Secrets and variables → Actions:
SUPABASE_URL
SUPABASE_SERVICE_KEY
COMPOSIO_API_KEY
VIEWPORT_RELAY_URL
VIEWPORT_LICENCE_KEY
The last two came with your licence and are what let the publish job reach TikTok. Add GROQ_API_KEY as well if you want written insights — free at console.groq.com.
Create a token so the app can start that job. Settings → Developer settings → Personal access tokens → Fine-grained tokens:
Copy it now; GitHub will not show it again.
From keyup-scheduler/:
npm install
npx wrangler login
Name your Worker. In wrangler.jsonc, change "name" and set GITHUB_REPO to your repository:
"name": "my-viewport",
"vars": {
"GITHUB_REPO": "your-username/your-repo"
}
Add your secrets. Each command prompts for the value:
npx wrangler secret put SUPABASE_URL
npx wrangler secret put SUPABASE_SERVICE_KEY
npx wrangler secret put GITHUB_DISPATCH_TOKEN
npx wrangler secret put SCHEDULER_PASSWORD
npx wrangler secret put SCHEDULER_TOKEN_SECRET
npx wrangler secret put COMPOSIO_API_KEY
npx wrangler secret put VIEWPORT_RELAY_URL
npx wrangler secret put VIEWPORT_LICENCE_KEY
SCHEDULER_PASSWORD is what you will type to open Viewport — choose a real one. SCHEDULER_TOKEN_SECRET signs your session and nobody ever types it, so make it long and random:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Add GROQ_API_KEY here too if you are using insights.
Deploy:
npx wrangler deploy
Wrangler prints your URL. Open it, enter your password, and you are in.
Back in keyup-scheduler-cron/, run the check with your GitHub token — the one part it could not see before:
GITHUB_REPO=your-username/your-repo GITHUB_DISPATCH_TOKEN=your-token npm run doctor
Everything should read [ ok ]. If not, the line names the service and what to do.
Open Viewport → Settings → Connect next to each of Instagram, YouTube and TikTok. Each opens that platform's own login in a new tab; Viewport never sees your password. The row turns green when it is done.
Schedule something small a few minutes out, to Instagram or YouTube only.
Once a post has gone out correctly, set YOUTUBE_DEFAULT_PRIVACY=public.
Run npm run doctor first. It exists so you do not have to guess which of four services broke.
| What you see | What it usually is |
|---|---|
| App opens but everything is empty | The anon key instead of service_role, or schema.sql was never run |
| Upload fails | The keyup-videos bucket is missing or not public |
| Posts sit at "pending" forever | The GitHub token lacks Actions: read and write, or GITHUB_REPO is wrong |
| Instagram refuses the post | The account is personal, not Business or Creator |
| TikTok will not connect | VIEWPORT_RELAY_URL or VIEWPORT_LICENCE_KEY is missing or mistyped |
| TikTok says the daily limit is full | Fifteen posts per day per licence. It resets; the post waits in the queue |
| Analytics numbers look old | They are. Refresh asks for new ones |
Skip this unless you have no licence and want TikTok anyway.
Instagram and YouTube work either way — Composio supplies those OAuth apps. TikTok does not work that way. TikTok only lets an unaudited app post to a private account, so to post publicly on your own you must register a TikTok developer app, own a domain and serve your terms and privacy pages from it, verify that domain with TikTok, record a demo video of the whole flow, give their reviewer a working login, and wait — usually days, sometimes two weeks.
Once TikTok has approved your app, add it as an auth config in your Composio project, and leave VIEWPORT_RELAY_URL and VIEWPORT_LICENCE_KEY out entirely. Viewport then posts through your app instead of ours.
Viewport runs in any browser. The desktop app is a thin window around the same site, so it stays current by itself.
From keyup-scheduler-desktop/, set APP_URL at the top of main.js to your Worker URL, then:
npm install
npm run build:icon
npm run dist
The installer lands in the output directory named in package.json.