Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions deploy/openpost/.env.openpost.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# OpenPost Reference Deployment Environment
# Upstream: https://github.com/getopenpost/openpost (v4.12.0)

OPENPOST_PORT=8080
OPENPOST_JWT_SECRET=generate-a-secure-random-32-byte-secret
OPENPOST_ENCRYPTION_KEY=generate-a-secure-random-32-byte-key
OPENPOST_API_TOKEN=generate-a-secure-random-token-for-pao-hubpro-access
OPENPOST_STORAGE_PATH=/data/media

# Social Publishing Control Plane (Pao-hubPro side)
SOCIAL_PUBLISHING_ENABLED=true
OPENPOST_BASE_URL=http://localhost:8080
OPENPOST_API_TOKEN=generate-a-secure-random-token-for-pao-hubpro-access
OPENPOST_TRANSPORT=hybrid

29 changes: 29 additions & 0 deletions deploy/openpost/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Self-Hosted OpenPost Deployment for Pao-hubPro

Phase 20.60 integrates Pao-hubPro with OpenPost (`https://github.com/getopenpost/openpost`), an AGPL-3.0-licensed open-source multi-platform social media publishing suite.

## Architecture Boundary

Pao-hubPro and OpenPost run as separated services communicating via HTTP REST API and MCP:
- **Pao-hubPro**: Orchestration, policy evaluation, human approval workflow, master content briefs, rendition planning, audit logging, and cross-platform analytics.
- **OpenPost**: Provider OAuth connections, provider token storage, delivery execution, and social network API interaction (X, Mastodon, Bluesky, LinkedIn, Threads, Instagram, TikTok, YouTube, Discord).

## Quick Start

```bash
cd deploy/openpost
cp .env.openpost.example .env
# Edit .env with your secrets
docker compose -f docker-compose.openpost.yml up -d
```

Verify service health:
```bash
curl http://localhost:8080/api/v1/health
```

Register instance in Pao-hubPro:
```bash
ocx social instances register --name "Production OpenPost" --url "http://localhost:8080"
```

31 changes: 31 additions & 0 deletions deploy/openpost/docker-compose.openpost.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: "3.8"

services:
openpost:
image: ghcr.io/getopenpost/openpost:v4.12.0
container_name: openpost-publishing
restart: unless-stopped
ports:
- "8080:8080"
environment:
- PORT=8080
- DATABASE_URL=sqlite:///data/openpost.db
- JWT_SECRET=${OPENPOST_JWT_SECRET}
- ENCRYPTION_KEY=${OPENPOST_ENCRYPTION_KEY}
- API_TOKEN=${OPENPOST_API_TOKEN}
- STORAGE_PATH=/data/media
- MCP_ENABLED=true
- MCP_PORT=8081
volumes:
- openpost-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s

volumes:
openpost-data:
driver: local

13 changes: 13 additions & 0 deletions docs/legal/openpost-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Legal and Licensing Boundary — OpenPost Integration

**Upstream Project:** OpenPost (`https://github.com/getopenpost/openpost`)
**License:** AGPL-3.0-only
**Integration Boundary:** Network Service Boundary (HTTP API / MCP)

## Engineering Policy

1. **No Code Merging:** OpenPost source code is not copied, vendored, or compiled into Pao-hubPro core repositories.
2. **Network Protocol Boundary:** All interactions between Pao-hubPro and OpenPost occur across a network protocol boundary via standard HTTP REST requests and Model Context Protocol (MCP) tool executions.
3. **Deployment Separation:** OpenPost is deployed as an independent container or service process. Pao-hubPro acts solely as a client orchestrator.
4. **Secret Isolation:** Social media provider credentials (OAuth tokens, refresh tokens, client secrets) are stored exclusively in OpenPost's persistent storage and are never exposed or synchronized to Pao-hubPro databases or agent context.

12 changes: 12 additions & 0 deletions gui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import CodexSet from "./pages/CodexSet";
import Integrations from "./pages/Integrations";
import Startup from "./pages/Startup";
import RemoteWorkspace from "./pages/RemoteWorkspace";
import { Skills } from "./pages/Skills";
import { Security } from "./pages/Security";
import { Credentials } from "./pages/Credentials";
import { Social } from "./pages/Social";
import ErrorBoundary from "./components/ErrorBoundary";
import { SidebarGithubRow } from "./components/sidebar-github-row";
import { IconGrid, IconServer, IconBoxes, IconBot, IconList, IconActivity, IconHardDrive, IconCodex, IconMenu, IconSun, IconMoon, IconMonitor, IconGlobe, IconPower, IconX, IconRefresh} from "./icons";
Expand All @@ -33,6 +37,10 @@ const PAGE_TKEY: Record<Page, TKey> = {
providers: "nav.providers",
models: "nav.models",
subagents: "nav.subagents",
skills: "nav.skills",
security: "nav.security",
credentials: "nav.credentials",
social: "nav.social",
logs: "nav.logs",
usage: "nav.usage",
storage: "nav.storage",
Expand Down Expand Up @@ -453,6 +461,10 @@ export default function App() {
{page === "remote" && <RemoteWorkspace apiBase={sharedBase} hubOrigin={targets.shared.serverOrigin} />}
{page === "codex-set" && <CodexSet apiBase={sharedBase} />}
{page === "integrations" && <Integrations apiBase={sharedBase} machineApiBase={machineBase} connected={targets.connected} />}
{page === "skills" && <Skills apiBase={sharedBase} />}
{page === "security" && <Security apiBase={sharedBase} />}
{page === "credentials" && <Credentials apiBase={sharedBase} />}
{page === "social" && <Social apiBase={sharedBase} />}
</>
)}
</ErrorBoundary>
Expand Down
35 changes: 35 additions & 0 deletions gui/src/app-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export type Page =
| "providers"
| "models"
| "subagents"
| "skills"
| "security"
| "credentials"
| "social"
| "logs"
| "usage"
| "storage"
Expand All @@ -21,6 +25,10 @@ export const VALID_PAGES = new Set<Page>([
"providers",
"models",
"subagents",
"skills",
"security",
"credentials",
"social",
"logs",
"usage",
"storage",
Expand Down Expand Up @@ -107,11 +115,38 @@ export const INTEGRATION_TAB_HASHES = [
"integrations/cline",
] as const;


export const SKILLS_TAB_HASHES = [
"skills/marketplace", "skills/registry", "skills/editor", "skills/matrix",
"skills/agents", "skills/nodes", "skills/drift", "skills/reviews", "skills/audit",
] as const;

export const SECURITY_TAB_HASHES = [
"security/authorizations", "security/scopes", "security/campaigns", "security/approvals",
"security/findings", "security/evidence", "security/skills", "security/tools",
"security/mcp", "security/policies", "security/audit",
] as const;

export const CREDENTIALS_TAB_HASHES = [
"credentials/list", "credentials/providers", "credentials/oauth", "credentials/pool",
"credentials/health", "credentials/quota", "credentials/policies",
"credentials/approvals", "credentials/audit",
] as const;

export const SOCIAL_TAB_HASHES = [
"social/accounts", "social/publications", "social/approvals",
"social/jobs", "social/analytics", "social/instances", "social/audit",
] as const;

export function hashBelongsToPage(rawHash: string, page: Page): boolean {
return rawHash === page
|| (page === "logs" && rawHash === "logs/debug")
|| (page === "codex-set" && rawHash === "codex-set/prompt")
|| (page === "models" && (MODELS_TAB_HASHES as readonly string[]).includes(rawHash))
|| (page === "skills" && (SKILLS_TAB_HASHES as readonly string[]).includes(rawHash))
|| (page === "security" && (SECURITY_TAB_HASHES as readonly string[]).includes(rawHash))
|| (page === "credentials" && (CREDENTIALS_TAB_HASHES as readonly string[]).includes(rawHash))
|| (page === "social" && (SOCIAL_TAB_HASHES as readonly string[]).includes(rawHash))
|| (page === "dashboard"
&& (rawHash === DASHBOARD_UPDATE_HASH || (DASHBOARD_TAB_HASHES as readonly string[]).includes(rawHash)))
|| (page === "integrations"
Expand Down
Loading
Loading