From c24ea815f3aadaf18df343a5a24e61648d2377ed Mon Sep 17 00:00:00 2001 From: "Angelo B. J. Luidens" Date: Wed, 3 Jun 2026 15:31:02 -0400 Subject: [PATCH] docs: record Gate 0.8 resolution + Gate 0.9 LinkedIn app / CMA progress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deferred-gates.md: Gate 0.8 RESOLVED — clean-room Postgres 17 + Redis 7 in a dedicated Dokploy project; service names, hosts, env-var locations recorded. - linkedin-apps.md: app created (Client ID 78s8f53y5spyo4), company-page + business-email verified, Community Management API Development Tier submitted (review in progress). Notes that other products grey out while CMA is pending. - docs/handoffs/2026-05-13-handoff.md: committed (was untracked). Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/deferred-gates.md | 38 ++++-- docs/handoffs/2026-05-13-handoff.md | 197 ++++++++++++++++++++++++++++ docs/linkedin-apps.md | 98 ++++++++++---- 3 files changed, 300 insertions(+), 33 deletions(-) create mode 100755 docs/handoffs/2026-05-13-handoff.md diff --git a/docs/deferred-gates.md b/docs/deferred-gates.md index 4b64822..5ffc62a 100644 --- a/docs/deferred-gates.md +++ b/docs/deferred-gates.md @@ -4,15 +4,37 @@ Items that could not be completed in the Stage 0 execution window and must be re ## Gate 0.8 — Postgres + Redis on PaaS -**Status:** Deferred 2026-04-19. -**Reason:** Dokploy MCP tools (`mcp__dokploy__*`) were not loaded in the Stage 0 session, and `DOKPLOY_API_KEY` was not in the WSL environment. Direct API query not possible in-session. -**Blocks:** Stage 2 (database + API) — the scheduler and admin cannot run without a Postgres + Redis pair. -**Resolution options:** -1. **Recommended:** Restart Claude Code with the vault's `.mcp.json` Dokploy MCP attached; run `mcp__dokploy__list_applications` / equivalent against `sg-paas-s1.stargue.net` to verify or provision. -2. Angelo confirms via Dokploy web UI and reports service names + connection hostnames. -3. Provision fresh Postgres + Redis services in the `stargue-publishing-engine` Dokploy stack (clean room; isolates blast radius from other PaaS apps). +**Status:** ✅ RESOLVED 2026-06-03 (via Dokploy MCP, resolution option 3 — clean-room stack). +**Reason (historical):** Dokploy MCP tools (`mcp__dokploy__*`) were not loaded in the Stage 0 session, and `DOKPLOY_API_KEY` was not in the WSL environment. Direct API query not possible in-session. +**Blocks (now cleared):** Stage 2 (database + API) — the scheduler and admin cannot run without a Postgres + Redis pair. -**Acceptance when resolved:** this file updated with service names, hostnames, and connection-string env-var locations (Dokploy secrets). +### Resolution (2026-06-03) + +Provisioned a dedicated clean-room Dokploy project on `sg-paas-s1.stargue.net`, isolating blast radius from other PaaS apps (option 3). + +| Item | Value | +|---|---| +| Dokploy project | `stargue-publishing-engine` (`projectId: Y6R_4ML2N0Vsztk0UVi1W`) | +| Environment | `production` (`environmentId: hkvob7vx02lzkn3GhpHMD`) | +| Postgres service | "Publishing Engine Database" (`postgresId: O_BjqkxljdMU3xLb3q09M`) — image `postgres:17`, status `done` | +| Postgres internal host | `stargue-pubeng-postgres-n9c1gc` (Docker network alias) port `5432` | +| Postgres db / user | `publishing_engine` / `publishing_engine` | +| Postgres volume | `stargue-pubeng-postgres-n9c1gc-data` → `/var/lib/postgresql/data` | +| Redis service | "Publishing Engine Redis" (`redisId: fZR9Bdx8-3d99uOrrqFe5`) — image `redis:7`, status `done` | +| Redis internal host | `stargue-pubeng-redis-9tebfd` (Docker network alias) port `6379` | +| Redis volume | `stargue-pubeng-redis-9tebfd-data` → `/data` | + +**Connection-string env-var locations (NOT stored in git):** +- `DATABASE_URL` = `postgres://publishing_engine:@stargue-pubeng-postgres-n9c1gc:5432/publishing_engine` +- `REDIS_URL` = `redis://default:@stargue-pubeng-redis-9tebfd:6379` +- Secret **values** live in Dokploy (retrieve via `mcp__dokploy__postgres-one` / `mcp__dokploy__redis-one`). They will be injected as Dokploy secrets on the **app** services (`apps/mcp-linkedin`, `apps/scheduler`, `apps/admin`) once those are created in Stage 5+. No app service exists yet, so there is nowhere to attach them today. + +**Remaining to fully unblock live DB tests:** +1. Run `0000_initial_schema.sql` against the new DB. The internal hostname is only resolvable inside the Dokploy Docker network, so either (a) temporarily add an external port via `mcp__dokploy__postgres-saveExternalPort` and run `bun run db:migrate` from WSL with the external `DATABASE_URL`, then remove the port; or (b) run the migration from a one-shot container inside the stack. +2. Run `bun run db:migrate:rehearse` (forward-then-rollback row-count hash check). +3. Re-enable `INTEGRATION=1`-gated tests with the connection strings wired. + +**Acceptance (met):** service names, hostnames, and connection-string env-var locations recorded above; secret values held in Dokploy, not committed. ## Gate 0.9 — LinkedIn Developer Portal apps diff --git a/docs/handoffs/2026-05-13-handoff.md b/docs/handoffs/2026-05-13-handoff.md new file mode 100755 index 0000000..7ccd6ea --- /dev/null +++ b/docs/handoffs/2026-05-13-handoff.md @@ -0,0 +1,197 @@ +--- +created: 2026-05-13T12:35 +updated: 2026-05-13T12:35 +tags: +--- + +# Publishing Engine — Session Handoff 2026-05-13 + +**Purpose:** let the next session pick up cold with full state of the Stargue Publishing Engine build. Source: this vault note + mirror at `docs/handoffs/2026-05-13-handoff.md` in the repo. + +**Repo:** `https://git.stargue.net/admin/stargue-publishing-engine` (local: `/home/devuser/projects/stargue-publishing-engine/` via WSL Debian) +**Plan v2 (SSoT):** [[Plan - Phase 1 Automated Publishing Engine|Plan - Phase 1 Automated Publishing Engine.md]] (vault) + `docs/plans/2026-04-19-phase1-plan.md` (repo mirror) +**PRD:** [[Publishing Engine PRD]] +**Panel verdicts:** `docs/plans/bmad-panel-verdicts-publishing-engine-phase1-20260419.json` (1 APPROVE, 6 REVISE, 0 REJECT — all principles ≥3) +**Doctrine:** `.clinerules/12-foundational-principles.md` adopted from DQMS + +--- + +## Where we stand — quick read + +| Stage | Status | Commit | Tests | Notes | +|---|---|---|---|---| +| 0 — Governance + scaffolding | ✅ Complete | `1dc1a1a` (2026-04-19) | n/a | 11 workspaces typecheck clean | +| 1 — Shared packages | ✅ Complete | `e529651` (2026-04-26) | 50/50 | 4 packages: schema, sanitize, linkedin-client (stubs), observability | +| 2 — DB + middleware + API | 🟡 Partial | `c73b7e4` (2026-04-26) | 79/79 cumulative | Live-DB integration tests blocked by **gate 0.8** | +| 3 — LinkedIn MCP server | 🟡 Partial | `7aa3137` (2026-04-26) | 106/106 cumulative | Live-LinkedIn blocked by **gate 0.9** | +| 4 — Scheduler + cadence | 🟡 Partial | `a4e4306` (2026-04-26) | 117/117 cumulative | BullMQ + integration blocked by **gates 0.8 + 0.9** | +| 5 — Admin dashboard (Next.js + A11y) | ❌ Not started | — | — | Independent of gates; can start anytime | +| 6 — stargue-com/.net integration | ❌ Not started | — | — | Independent of gates | +| 7 — Content + metrics bootstrap | ❌ Not started | — | — | 7.1 + 7.2 independent of gates | + +**Last activity:** 2026-04-26 (commit `a4e4306`). Repo idle since. Today is 2026-05-13 — ~2.5 weeks of pause. + +--- + +## Deferred gates (the real blockers) + +### Gate 0.8 — Postgres + Redis on PaaS + +**Status:** Dokploy MCP was loaded in the 2026-04-19 session but verification not completed before pause. **Resume action:** with Dokploy MCP attached, run `mcp__dokploy__project-all` → identify Stargue PaaS project → `mcp__dokploy__postgres-search` + `mcp__dokploy__redis-search` to see existing instances; provision new ones in a `stargue-publishing-engine` project if none are shareable. + +**Why it blocks:** Stage 2 live-DB integration tests, Stage 3 token-store live tests, Stage 4 BullMQ worker, Stage 5 admin auth flow. + +**Next steps when resolved:** +1. Run `0000_initial_schema.sql` migration on the new DB +2. Run `bun run db:migrate:rehearse` (script lives at `packages/schema/db/migrations/rehearse.ts`) +3. Wire connection strings into Dokploy secrets `DATABASE_URL`, `REDIS_URL` +4. Re-enable the integration tests gated behind `INTEGRATION=1` + +### Gate 0.9 — LinkedIn Developer Portal apps + +**Status:** Pending Angelo's manual action. Checklist ready at `docs/linkedin-apps.md`. + +**Why it blocks:** Stage 3.1–3.7 live posting, Stage 4 end-to-end publish, Stage 7.4 first scheduled post. + +**Next steps when resolved:** +1. Angelo registers app at linkedin.com/developers/ linked to Stargue Company Page `urn:li:organization:2605890` +2. Enables Sign-In with OpenID Connect + Share on LinkedIn (instant self-service) +3. Submits Community Management API + MDP partner-access requests (multi-week review — file ASAP to start the clock) +4. Drops `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`, `LINKEDIN_TOKEN_ENCRYPTION_KEY` into Dokploy secrets +5. Update `docs/linkedin-apps.md` registry with app IDs + +--- + +## What's already built (don't re-invent) + +### `packages/schema` (full SSoT for data shapes) +- `frontmatter.ts` — Zod schema for vault frontmatter (status, language, outlets[], sanitize, scheduled, version) +- `db.ts` — Drizzle schema for 7 tables: content, publications (with `idempotency_key UNIQUE`), approvals, metrics, linkedin_tokens, audit, outlet_feature_flags +- `rate-limit.ts` — token-bucket implementation, pure-function +- `csrf.ts` — double-submit cookie pattern +- `authz.ts` — Cerbos-equivalent policy primitives +- `api-contracts.ts` — Zod request/response schemas for admin API +- `drizzle.config.ts` + `db/migrations/0000_initial_schema.sql` + `0000_initial_schema.down.sql` + `db/migrations/rehearse.ts` + +### `packages/sanitize` (fail-closed content pipeline) +- `rules.ts` — PRIVATE_PATH_PREFIXES (Family Matters, Financial Matters, Journal, Clients NDA…), PRIVATE_PATH_PATTERNS, PRIVATE_TAGS (`#private`, `#heal-internal`, `#confidential`), OUTLET_LENGTH_LIMITS +- `errors.ts` — SanitizeError with codes: PRIVATE_PATH_BLOCKED, PRIVATE_TAG_BLOCKED, WIKILINK_TO_PRIVATE_PATH, OUTLET_LENGTH_EXCEEDED +- `index.ts` — `sanitize(markdown, opts)` returns `{ body, contentHash, warnings }` — strips wikilinks/embeds/dataview/callouts, enforces blocklist + tag firewall + length, computes SHA-256 hash +- `corpus.test.ts` — Stage 1.2 DoD test: 12 private + 6 clean fixtures round-trip correctly + +### `packages/linkedin-client` (Posts API client + token store) +- `types.ts` — LinkedIn Posts API request/response types (Posts API v202404, not legacy UGC) +- `client.ts` — HTTP client; respects `LinkedIn-Version: 202404` + `X-Restli-Protocol-Version: 2.0.0` +- `token-store.ts` — encrypted persistence; **uses AES-256-GCM via `node:crypto`** (NOT libsodium — see D-001 in `docs/deferred-gates.md` for the deviation rationale: libsodium-wrappers ESM bug under Bun) +- `client.test.ts` + `token-store.test.ts` — 4/4 round-trip tests pass + +### `packages/observability` (Pino + correlation IDs) +- `index.ts` — Pino logger with PII-redaction paths (access_token, refresh_token, client_secret, *_ct fields), `newCorrelationId()`, `child(bindings)` helper + +### `apps/mcp-linkedin` (Stage 3 partial — structural complete, live blocked) +- `server.ts` — `createServer` factory pattern, injectable deps (KillSwitch, AdvisoryLock); MCP_SPEC_VERSION pinned `2024-11-05`; `validateToolCall`, `listTools`, `outletForAuthorUrn` helpers +- `tools.ts` — 9 Zod tool schemas: whoami, auth_status, create_post, create_article, upload_media, create_post_with_media, delete_post, get_post_metrics, get_profile_stats +- `oauth.ts` — auth-code URL builder + HMAC-protected state parameter (no PKCE — LinkedIn 3LO doesn't support it) +- `kill-switch.ts` — `outlet_feature_flags` reader with cache invalidation +- `refresh-lock.ts` — Postgres advisory-lock primitive; **4-concurrent rotation test passes** (TEA gap 3 closed) + +### `apps/scheduler` (Stage 4 partial — pure pieces complete) +- `cadence.ts` — Beta(1,19) posterior optimizer, `propose(history, config) → [Proposal]`; all 4 thresholds verified (≥8 samples, ≥4 weeks, ≥20% ratio, disjoint 95% CIs); never auto-applies (RBR) +- `publish-loop.ts` — state-machine transition function `pending→queued→dispatching→published`, retry-vs-DLQ branch, cancellation path +- `worker.ts` — entry stub (BullMQ wiring blocked by Redis on gate 0.8) + +### `apps/admin` (not started) +- `src/index.ts` — stub only. Next.js scaffold + Authentik SSO + queue/publications/auth/metrics pages all pending. + +--- + +## Architecture decisions worth remembering + +1. **D-001 token cipher: AES-256-GCM, not libsodium secretbox.** Bun ESM bug in `libsodium-wrappers`. Same AEAD security profile. Documented in `docs/deferred-gates.md`. Remediation path noted if libsodium fixes the bug. +2. **Scheduler imports `linkedin-client` directly** — MCP transport is stdio-only for interactive Claude Code use. No HTTP MCP transport in Phase 1 (architect gap 2). +3. **`apps/api` collapsed into `apps/admin`** — Next.js App Router route handlers cover the API surface; less infra (architect gap 1). +4. **LinkedIn Posts API `/rest/posts`** — NOT the legacy `/v2/ugcPosts`. Headers: `LinkedIn-Version: 202404`, `X-Restli-Protocol-Version: 2.0.0`. +5. **No PKCE in OAuth** — LinkedIn 3LO doesn't document it; confidential-client with HMAC `state` parameter is what we use. +6. **`idempotency_key UNIQUE` on `publications`** — sha256(content_id || outlet || scheduled_at). Prevents double-post on retry. +7. **Kill-switch via `outlet_feature_flags` table** — fast disable without redeploy. Cached at MCP server with invalidation on table change. + +--- + +## Next session — priority order + +### Path A: Resolve gates first (recommended if Angelo + Dokploy access available) +1. **Gate 0.8** — Dokploy MCP query → provision Postgres + Redis in `stargue-publishing-engine` project on `sg-paas-s1.stargue.net` +2. **Gate 0.9** — Angelo files LinkedIn Dev Portal apps (~30 min for OIDC + Share; submit Community Management API + MDP requests to start the partner-approval clock) +3. With both gates open, unblock Stages 2.6, 3.4–3.9, 4.1–4.3 live integration tests +4. Move to Stage 5 (admin dashboard) + +### Path B: Continue parallel work (if gates remain blocked) +1. **Stage 5** — Admin dashboard scaffold: Next.js App Router + shadcn/ui + Nine Laws design tokens; pages `/queue`, `/publications/:id`, `/auth/linkedin`, `/metrics`; A11y CI (axe-core + @axe-core/playwright). Authentik stub for local dev. +2. **Stage 6** — Add `@stargue/sanitize` + `@stargue/schema` as workspace deps to stargue-com + stargue-net; replace ad-hoc frontmatter parsing; assert zero wikilinks in rendered HTML. +3. **Stage 7.1** — Run `/market audit` baseline on stargue.com + stargue.net; save to `docs/marketing-baseline-2026-05.json`. +4. **Stage 7.2** — Draft 7 Cs posts 2–8 (Commitment, Co-Creation, Connection, Communication, Celebration & Course Correction, Caring & Crossing, Synthesis) — long-form for stargue.com + LinkedIn-optimized variants. Source content from `7 Cs LinkedIn Series - Continuation Plan.md`. Run each through autoresearch optimization to composite ≥8. + +### Path C: Mixed (most realistic) +- Angelo does Gate 0.9 in parallel (~30 min self-service; partner approvals run async for weeks) +- Claude provisions Gate 0.8 via Dokploy MCP +- Concurrently start Stage 5 and 7.2 + +--- + +## Open items / things to verify + +1. **MDP + Community Management API timeline** — once filed, track approval state weekly. MDP rejection is the trigger for the 60-day-re-auth fallback. +2. **Authentik instance reachability** — plan §8 q3 said "shared instance for all Stargue services"; needs runtime verification before Stage 5.2. +3. **Neon free-tier mirror** — Stage 7.5 DR test pending. Set up nightly `pg_dump → Neon` cron when gate 0.8 resolved. +4. **stargue-com/.net package.json sync** — collision was fixed (commit `fd43214` on stargue-net) — verify both repos still align before Stage 6. +5. **LinkedIn algorithm-reach risk** — plan §9 row added by analyst panel. Once first 4 posts ship, monitor engagement-delta vs Angelo's existing manual baseline; if suppression detected, fall back to draft-and-prompt workflow. + +--- + +## Files to read first in next session + +In order, for cold start: +1. This handoff +2. `docs/plans/2026-04-19-phase1-plan.md` (in the repo) — full plan v2 +3. `docs/deferred-gates.md` — for D-001 + gate status +4. `CLAUDE.md` in the repo — for governance + canonical LinkedIn facts +5. Last 4 commits via `git log` — for exact stage progress +6. `docs/linkedin-apps.md` — for Angelo's manual checklist + +--- + +## Memory notes already saved + +- `project_publishing_engine.md` — overall project state +- D-001 cipher deviation is in `docs/deferred-gates.md` (in repo) — no separate memory note needed since the deviation is project-local +- BMAD panel verdicts cached at `docs/plans/bmad-panel-verdicts-publishing-engine-phase1-20260419.json` + +--- + +## Commands cheatsheet + +```bash +# WSL Debian — always run there +wsl -d Debian -- bash -c 'cd /home/devuser/projects/stargue-publishing-engine && ' + +bun install # workspace deps +bun run typecheck # all 11 workspaces +bun run test # 117/117 currently passing +bun run db:migrate # apply Drizzle migrations (needs DATABASE_URL) +bun run db:migrate:rehearse # expand-then-contract rehearsal + +# Gitea +git push # auto-deploys via Dokploy webhook (Phase 0 sites); + # Publishing Engine apps deploy on separate Dokploy stack (Stage 5+) +``` + +--- + +## Related + +- [[Publishing Engine PRD]] +- [[Plan - Phase 1 Automated Publishing Engine]] +- [[7 Cs LinkedIn Series - Continuation Plan]] +- [[Design Philosophy - The Nine Laws of the Hearth]] +- [[Outlet Profiles/LinkedIn]] +- [[Implementation Progress]] — update with this handoff's status table after the next session diff --git a/docs/linkedin-apps.md b/docs/linkedin-apps.md index 5efcaa9..6881000 100644 --- a/docs/linkedin-apps.md +++ b/docs/linkedin-apps.md @@ -2,45 +2,93 @@ **Status tracker for Stargue's LinkedIn apps.** Every row must stay current — Claude Code verifies against this file during Stage 0 DoD 0.9. -## Applications +## App: Stargue Publishing Engine (created 2026-06-03) -| App | Product | Purpose | Scopes granted | State | App ID | Approval date | Notes | -|---|---|---|---|---|---|---|---| -| Stargue Publishing Engine — Sign In | Sign In with LinkedIn using OpenID Connect | Identity / `whoami` | `openid profile email` | **TODO: register** | — | — | Standard — no approval required beyond Dev Portal self-service | -| Stargue Publishing Engine — Member Share | Share on LinkedIn | Personal-profile posts (Angelo) | `w_member_social` | **TODO: register** | — | — | Standard — attached to the same app as Sign In | -| Stargue Publishing Engine — Org Share | Community Management API | Company-page posts (`urn:li:organization:2605890`) | `w_organization_social`, `r_organization_social` | **TODO: apply** | — | — | Requires LinkedIn partner approval; submit via Dev Portal "Request access" | -| Stargue Publishing Engine — Refresh | Marketing Developer Platform (MDP) | Programmatic refresh tokens (eliminates 60-day re-auth) | (extends above) | **TODO: apply** | — | — | Requires LinkedIn partner approval; multi-week review | +- **Client ID:** `78s8f53y5spyo4` +- **Client Secret:** present on Auth tab (Primary Client Secret) — copy to Dokploy secret `LINKEDIN_CLIENT_SECRET`, never commit +- **App type:** Standalone app +- **LinkedIn Page:** Stargue (`urn:li:organization:2605890`) — association is permanent, **NOT yet verified** (see below) +- **Privacy policy URL:** https://stargue.com/privacy (live 2026-06-03) ✅ +- **Business email:** `info@stargue.com` — **verified 2026-06-03** ✅ (required for CMA) +- **Access-token TTL:** 2 months / 5,184,000 s (confirms the 60-day re-auth design; programmatic refresh is MDP-gated) +- **Authorized redirect URLs:** none added yet +- **OAuth scopes:** none yet + +## Status update — 2026-06-03 13:15 + +**Community Management API (Development Tier) = SUBMITTED → "Review in progress."** Access form completed (legal name `Stargue N.V.`, Curaçao address Dwarsweg 9 Julianadorp LP71, website stargue.com, 3 use cases: Page management + Page analytics + Profile management, first-party compliance description). Decision comes by email to `info@stargue.com`. (Note: the Qualtrics "session expired" end-screen was a false alarm — it did submit.) + +**⚠️ NEW OBSERVATION — once CMA was requested, ALL other products greyed out** (Share on LinkedIn, Sign In with OpenID Connect, etc. now show disabled "Request access"). **Uncertain (not yet confirmed) whether this is:** +- (a) **temporary** — a lock while CMA is in review, that lifts on approval; or +- (b) **permanent** — CMA apps are meant to be CMA-only (consistent with FAQ #1/#2 treating CMA + other products as a separate-app pattern). + +**Architecture decision deferred pending CMA outcome:** +- If other products **un-grey** after CMA approval → keep single app, add Sign In + Share here. +- If they **stay greyed** → **two-app split** after all: this app = CMA (company + profile posting via CMA), a **second app** = Sign In (OIDC identity) + Share on LinkedIn (`w_member_social`). +- **Possible simplification:** CMA's *Profile management* use case may already cover posting to Angelo's own profile, reducing/eliminating the need for a separate "Share on LinkedIn" product. Confirm when wiring OAuth scopes. + +## ⚠️ Why the Community Management API is greyed — VERIFIED (2026-06-03) + +The app has **no other products**, so FAQ #4 ("greyed because the app already has other products") is **not** the cause here. The real prerequisites — confirmed against LinkedIn docs and visible on the app's **Settings** tab — are: + +1. **Company-Page verification (PRIMARY GATE).** Settings shows *"This app is not verified as being associated with this company"* + a **Verify** button. A **super-admin of the Stargue Company Page** must approve the app↔page association. CMA stays greyed until done. **Dependency: Angelo must be, or have access to, a super-admin of the Stargue Page.** +2. **Business-email verification.** Settings banner: *"Update your business email…"*. The CMA access form requires a **verified business email**. +3. **Access-form requirements.** CMA is for **registered legal organizations, commercial use only**. Form asks for: legal org name, registered address (Curaçao), website, **privacy policy URL** (✅ live), verified business email. +4. **Secondary — product order (FAQ #4).** If an app *already has* other products, the CMA request greys out. Not our cause today (clean app), but it means: **request CMA before adding Share/Sign In**, or keep CMA on its own app. + +### Corrected recommendation: single app, CMA-first + +1. **Verify** the app ↔ Stargue Page (super-admin approves). +2. **Set + verify** the business email. +3. Request **Community Management API first** → complete access form → **Development Tier** (limits: 500 req/app, 100 req/member). +4. Apply **Standard Tier** (access form + screencast demonstrating each use case). +5. **Then** add **Share on LinkedIn** + **Sign In with OpenID Connect** (instant) for personal posting + identity. + +(Two-app split — CMA on its own app, Share/Sign In on another — remains a fallback if Page verification cannot be completed on this app.) + +## Products — observed state (2026-06-03 Products tab) + +| Product | Needed for | Self-service? | State on this app | +|---|---|---|---| +| Community Management API | Company-page posts (`w_organization_social`, `r_organization_social`) | No — needs verification + Dev→Standard | **🔒 Greyed** — pending Page verification + business email | +| Sign In with OpenID Connect | Identity / `whoami` (`openid profile email`) | Yes (instant) | Available — request AFTER CMA | +| Share on LinkedIn | Personal-profile posts (`w_member_social`) | Yes (instant) | Available — request AFTER CMA | +| Marketing Developer Platform / Advertising / Lead / Conversions | Programmatic refresh tokens; marketing | No (partner review) | Greyed; optional (60-day re-auth fallback exists) | + +Other products present but not needed: LinkedIn Ad Library, Verified on LinkedIn (available); Live Events, Events Mgmt, Member/Pages Data Portability (greyed). ## OAuth redirect URIs -- Production: `https://publishing.stargue.net/auth/linkedin/callback` +Add under the app's **Auth** tab (not yet done): +- Production: `https://publishing.stargue.net/auth/linkedin/callback` (note: `publishing.stargue.net` subdomain is Stage 5+; admin app not built yet) - Local dev: `http://localhost:3002/auth/linkedin/callback` ## Client credentials -Stored in Dokploy secrets on `sg-paas-s1.stargue.net`: -- `LINKEDIN_CLIENT_ID` -- `LINKEDIN_CLIENT_SECRET` -- `LINKEDIN_TOKEN_ENCRYPTION_KEY` (32-byte key for `crypto_secretbox`) +Dokploy secrets on `sg-paas-s1.stargue.net` (project `stargue-publishing-engine`, once app services exist in Stage 5+): +- `LINKEDIN_CLIENT_ID` = `78s8f53y5spyo4` +- `LINKEDIN_CLIENT_SECRET` = (from Auth tab — pending copy) +- `LINKEDIN_TOKEN_ENCRYPTION_KEY` (32-byte; **AES-256-GCM** per D-001 — see `deferred-gates.md`) ## Manual setup checklist (Stage 0.9) -1. Sign in to `https://www.linkedin.com/developers/` as Angelo (personal account). -2. Create app "Stargue Publishing Engine" linked to Stargue Company Page `urn:li:organization:2605890` (required for Community Management API eligibility). -3. Under **Products**, request: - - Sign In with LinkedIn using OpenID Connect (self-service; instant) - - Share on LinkedIn (self-service; instant) - - Community Management API (submit partner-access request — fill in use-case: "Programmatic posting of original content to Stargue Company Page for knowledge-management-as-a-service practice") - - Marketing Developer Platform (separate partner-access request — fill in use-case: "Automate posting and engagement-metric collection for owned content from a self-hosted publishing pipeline") -4. Under **Auth**, add redirect URIs above. -5. Copy Client ID and Client Secret to Dokploy secrets (never commit). -6. Update this file with the app ID. -7. Re-request Community Management API + MDP if denied initially, with more detailed use-case. +1. ✅ Sign in to `https://www.linkedin.com/developers/` as Angelo. +2. ✅ Create app "Stargue Publishing Engine" linked to Page `urn:li:organization:2605890` (Client ID `78s8f53y5spyo4`). +3. ✅ Privacy policy URL live at https://stargue.com/privacy. +4. ✅ **Verify app ↔ Company Page** (done 2026-06-03 — un-greyed all products). PRIMARY gate for CMA. +5. ✅ **Business email verified** — `info@stargue.com` (2026-06-03). +6. ☐ **← NEXT:** Request **Community Management API FIRST**: add product → complete access form (legal name Stargue Inc., Curaçao address, website, privacy URL, business email `info@stargue.com`) → Dev Tier → apply Standard Tier later (screencast). +7. ☐ THEN request **Sign In + Share on LinkedIn** (instant). +8. ☐ **Auth tab:** add redirect URIs above. +9. ☐ Copy **Client Secret** → Dokploy secret `LINKEDIN_CLIENT_SECRET` (never commit). +10. ☐ Keep this file current with app IDs + states. ## References - [LinkedIn Developer Portal](https://www.linkedin.com/developers/) +- [Community Management — Overview + FAQ (Microsoft Learn)](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/community-management-overview) +- [Organizations & Brands Overview (Microsoft Learn)](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/organizations) +- [Increasing Access — tiers & app review](https://learn.microsoft.com/en-us/linkedin/marketing/increasing-access) +- [Quick Start — Apply for API Access](https://learn.microsoft.com/en-us/linkedin/marketing/quick-start) - [Posts API](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api) -- [3-Legged OAuth Flow](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow) - [Programmatic Refresh Tokens](https://learn.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens) -- [Community Management API overview](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/)