pm-gpt Native ChatGPT and Codex App Research and Implementation Plan

Tracked in the private companion project:

  • product feature: pm-cli-website-0fx3;
  • research task: pm-cli-website-hmyb; and
  • architecture gate: pm-cli-website-wsp9.

Research date: 2026-07-11. This is an internal research and implementation plan for a privately developed, publicly hosted service. It does not record a completed app, plugin, MCP server, OAuth deployment, Caddy route, Cloudflare rule, or OpenAI submission.

The private remote unbraind/pm-gpt and its empty local checkout at /home/steve/container/pm-gpt were created during this research pass. No application files have been scaffolded.

Executive decision

Build pm-gpt as a hosted, tenant-aware pm service with a managed pm workspace as the default system of record. GitHub must be an optional provider integration, not the user identity, storage layer, or execution prerequisite. Users without a GitHub account must be able to create a pm account, create durable workspaces, use the supported pm CLI and SDK capabilities through ChatGPT or Codex, attach documents, export their data, and reconnect later.

The recommended product split is:

Product Purpose Where data and code run
pm-gpt hosted app Public ChatGPT/Codex app, managed workspaces, optional integrations, collaborative UI Private pm-gpt backend on unbrained infrastructure
pm-codex local plugin Repository-local, offline, private-network, and unrestricted developer workflows Customer machine under the Codex sandbox and approval policy
Secure MCP Tunnel lane Opt-in bridge from ChatGPT/Codex to a customer-controlled private pm server Outbound tunnel client on the customer host
pm CLI/SDK Portable local source of pm semantics and archive compatibility Local process or isolated hosted worker

Do not make the hosted app a remote shell around pm_run. The public MCP surface must expose focused, schema-stable tools backed by the pm SDK. Operations that execute tests, install packages, load extensions, run Git, or start subprocesses require a separate isolated job plane.

What this research adds to the upstream pm-cli plan

The official pm-cli repository already contains a strong native-plugin audit in fleet/pm-cli/docs/CHATGPT_CODEX_PLUGIN_IMPLEMENTATION.md, tracked by upstream items pm-n28t and pm-95d7. That audit correctly distinguishes a native Codex plugin from a ChatGPT app and finds defects in the current plugins/pm-codex package.

This document adds the product architecture that was intentionally left undecided there:

  1. pm-gpt is a separate publicly hosted service with a private, closed-source implementation repository; it is not a rename of the current local plugin.
  2. A first-class managed pm workspace replaces GitHub as the assumed persistence substrate.
  3. GitHub and other services attach through explicit provider adapters owned by pm-gpt.
  4. Full feature parity is split into a safe in-process action plane and an isolated execution plane.
  5. The existing Authentik and Caddy deployments are evaluated against the current OpenAI requirements.
  6. The public plugin package is treated as an inspectable client artifact even though the backend repository is private.

No new tracker item was added to the official pm-cli repository for this hosted product. The upstream SDK-first epic pm-usfg remains an implementation dependency because a hosted service should not bind itself to private CLI internals.

Research method and source currency

All external technical sources in this document were opened live on 2026-07-11. Primary sources were used for OpenAI, Caddy, Cloudflare, GitHub, and Authentik claims.

The OpenAI documentation skill first attempted the official Codex manual helper. The helper failed because the manual response did not include the expected x-content-sha256 header. The official OpenAI Developer Docs MCP server was then installed for future Codex sessions, but MCP registrations require a session restart. This pass therefore used the current official OpenAI and ChatGPT documentation pages directly. The upstream pm-cli clone was synchronized to commit ba09b155e7f66d4249f16ac90ba3f55bbc1807e8, whose subject is docs: plan native ChatGPT and Codex plugin alignment.

Core official sources:

The Apps SDK changelog was checked for changes through 2026-07-11. Material recent entries include server instructions, output-schema examples, MCP Apps compatibility, and the June 2026 permission controls. An older changelog statement that limited plugins to Codex is superseded by the current Plugins page, which documents the universal plugin directory across ChatGPT Work, ChatGPT desktop Work/Codex, Codex CLI, and the Codex IDE extension.

Current OpenAI product model

Plugin

A plugin is the distributed package. It can combine skills, an app, MCP server configuration, hooks, and assets. The required entry point is .codex-plugin/plugin.json. Current official and executable validation contracts use these root-level companions when applicable:

pm-gpt-plugin/
├── .codex-plugin/
│   └── plugin.json
├── .app.json
├── .mcp.json              # only if a separate direct MCP registration is proven necessary
├── skills/
└── assets/

The current built-in @plugin-creator contract accepts:

  • apps: "./.app.json" in plugin.json;
  • .app.json as an apps object whose entries contain an app id and optional category;
  • mcpServers: "./.mcp.json" or an inline server object; and
  • .mcp.json as a wrapped mcpServers object.

The OpenAI build page and current runtime examples have had naming discrepancies around MCP wrapper casing. Do not resolve that discrepancy from memory. Generate the first real package with the then-current @plugin-creator, validate it with the installed validator, and smoke it in every supported Codex surface.

App

An app is the MCP-backed live capability included by a plugin. The MCP server is mandatory; a custom UI is optional. ChatGPT reaches the server through a stable HTTPS /mcp endpoint or, for a private lane, a supported Secure MCP Tunnel.

For developer wiring, ChatGPT creates an identifier beginning with plugin_asdk_app. That real ID is placed in .app.json. Never invent or commit a personal placeholder app ID. Public submission later scans the production MCP URL directly.

Public distribution does not make the backend open source

The backend repository can remain private. However, users and reviewers can inspect a distributed plugin archive and all MCP tool metadata. Therefore:

  • put no backend source, signing key, OAuth secret, service credential, internal hostname, or reviewer password in the plugin package;
  • assume skill instructions, manifest fields, app IDs, logos, screenshots, starter prompts, and MCP descriptors are public;
  • keep all authorization and policy enforcement on the server; and
  • treat obfuscation as no protection.

Supported surfaces are not identical runtimes

The universal directory is shared, but execution differs:

  • ChatGPT web cannot launch a local stdio process from a repository.
  • Codex CLI and IDE can launch a plugin-provided local MCP server inside their host sandbox.
  • The ChatGPT desktop Work/Codex surface may expose both app and local-workflow experiences, but it must not be assumed to share local filesystem access with a web conversation.
  • A hosted app must derive tenant and workspace context from authenticated server state, never from an assumed local working directory.

Product requirements

Mandatory

  1. A user can register and use pm without GitHub.
  2. Every workspace has durable server-side identity independent of a repository URL.
  3. A user can export a complete, documented, CLI-compatible representation of their data.
  4. GitHub, GitLab, Gitea, archives, and future services are optional integrations.
  5. Private reads and all writes require authenticated authorization.
  6. Tool contracts are narrow, typed, idempotency-aware, and accurately annotated.
  7. Cross-tenant access is impossible by construction and continuously tested.
  8. Code-executing features are isolated from the MCP/API process.
  9. The service provides deletion, retention, audit, backup, restore, and incident procedures.
  10. The public product claims only capabilities proven on each OpenAI surface.

Explicit non-goals for the first release

  • No generic remote shell.
  • No public pm_run catch-all.
  • No arbitrary package or extension installation in the main API container.
  • No automatic access to credentials from a ChatGPT GitHub or Drive connector.
  • No GitHub account requirement.
  • No silent repository writes or background sync without explicit grants.
  • No submission before OAuth, tenancy, legal, security, and reviewer flows are production-ready.
  • No claim of full CLI parity while the upstream public SDK still lacks typed primitives for some domains.

Recommended architecture

ChatGPT / Codex / plugin skills / optional widget
                     │
                     │ MCP Streamable HTTP + OAuth access token
                     ▼
             pm-gpt.unbrained.dev
         Cloudflare → Caddy → pm-gpt API
                     │
        ┌────────────┼───────────────────────┐
        │            │                       │
        ▼            ▼                       ▼
 OAuth resource   MCP tool router       App UI resources
 validation       + policy engine       + legal/support
        │            │
        └────────────┼───────────────────────┐
                     ▼                       ▼
              Workspace service       Integration service
              + public pm SDK         GitHub/GitLab/Gitea/...
                     │                       │
        ┌────────────┼────────────┐          ▼
        ▼            ▼            ▼      Webhooks + jobs
 durable pm      metadata/     object
 workspace       audit DB      storage
        │
        ├── safe in-process SDK actions
        └── isolated job queue → disposable worker sandboxes
                                    ├── tests
                                    ├── package/extension operations
                                    ├── Git materialization
                                    └── import/export and heavy validation

Control plane and data plane

The API process is a control plane. It authenticates the caller, resolves the tenant and workspace, validates tool input, performs policy checks, invokes safe SDK actions, starts isolated jobs, and returns bounded results. It must not run untrusted repository scripts.

The data plane contains durable workspace state, attachments, audit records, integration credentials, and worker materializations. Every object is keyed by an opaque tenant and workspace ID. A model-visible item ID such as pm-ab12 is never sufficient authorization to locate data.

Service boundaries

Component Responsibility Must not do
Edge/Caddy TLS, routing, safe headers, redacted access logs, health routing Authorize pm business actions
OAuth resource server Validate tokens, issuer, audience, scopes, time bounds, revocation/policy Trust tool annotations as authorization
MCP router Publish schemas, choose handler, enforce request limits, shape results Accept arbitrary action names
Workspace service Resolve tenant/project, lock/version state, call public pm SDK Infer identity from user-provided paths
Integration service Store grants, mint provider tokens, receive webhooks, schedule sync Reuse ChatGPT connector credentials
Job service Queue, quota, cancel, observe, and expire jobs Execute workloads in the API container
Worker sandbox Materialize one workspace/job and run an allowlisted operation Reach another tenant or unrestricted network
Widget Review, selection, visualization, confirmation, progress Become the authoritative store

GitHub-optional managed workspace model

Workspace identity

Use opaque service identifiers:

tenant_id     organization or personal tenancy
principal_id  authenticated human or service identity
workspace_id  durable managed pm project
project_id    optional logical project within a workspace
provider_id   optional external integration installation

Never use GitHub owner/repository as the primary key. It can be an external binding on a workspace.

Authoritative storage recommendation

The first implementation should preserve pm filesystem compatibility while avoiding GitHub dependence:

  1. Keep one isolated, durable pm workspace tree per workspace on encrypted storage.
  2. Run public SDK operations against that workspace through a storage/workspace adapter.
  3. Keep tenancy, membership, OAuth grants, provider installations, idempotency records, job state, and audit indexes in PostgreSQL.
  4. Keep large attachments, import archives, exports, and optional snapshots in object storage.
  5. Serialize mutations per workspace or use explicit optimistic versions plus conflict retries.
  6. Generate complete signed exports containing pm data, attachments, manifest, format version, and checksums.

This is the lowest-risk bridge to current pm semantics. A later storage adapter may make PostgreSQL or object storage canonical, but only after round-trip parity proves that no pm history, links, comments, custom schema, extension data, or project configuration is lost.

The open Decision pm-cli-website-wsp9 must approve the final authoritative store. Do not quietly make an internal Git repository the database; Git can be an optional snapshot mechanism, but GitHub must remain absent from the core dependency chain.

Concurrency and integrity

Each mutation should include:

  • authenticated principal and effective workspace role;
  • workspace version or ETag;
  • client-generated idempotency key;
  • normalized operation name and safe parameter hash;
  • server request ID;
  • expected lifecycle state where applicable; and
  • audit reason for consequential operations.

Use a workspace lock or compare-and-swap version around filesystem mutations. Store the idempotency result long enough to make ChatGPT retries safe. The Apps SDK documentation explicitly warns that models may retry tool calls; duplicate creates, comments, claims, jobs, and provider writes must not occur.

Portability and no-GitHub user journey

A user with no GitHub account should be able to:

  1. Sign in to pm-gpt with a supported pm identity.
  2. Create an empty managed workspace or import a pm archive.
  3. Select the workspace in ChatGPT or Codex.
  4. Use context, planning, item lifecycle, annotations, schema/profile/config, history, and approved execution capabilities.
  5. Upload or create attachments without a repository.
  6. Export the full workspace at any time.
  7. Delete the workspace and receive a documented deletion outcome.
  8. Later attach GitHub or another provider without changing the workspace ID.

CLI interoperability needs an explicit client contract. The current CLI is filesystem-native and does not yet provide a generic hosted pm remote transport. Future implementation must choose one or more of:

  • a private pm-gpt CLI extension that talks to the hosted API;
  • explicit pull/push sync of signed pm archives;
  • a local sync daemon that materializes a managed workspace; or
  • an upstream storage-adapter capability if and when that becomes part of the public SDK.

Do not claim live CLI parity until one path has conflict, offline, resume, export, and version-skew tests.

Optional GitHub and connected-service integrations

Credential boundary

An OAuth grant to a ChatGPT connector belongs to ChatGPT. No official contract says that connector credentials are delegated to a third-party MCP server. Model orchestration may ask both plugins to act, but it is not a secure synchronization API.

For reliable repository access, pm-gpt needs its own explicit provider installation and authorization flow. The user must see which provider, account, repositories, permissions, and sync direction are being granted.

GitHub recommendation

Use a dedicated GitHub App rather than a broad OAuth App. GitHub recommends GitHub Apps for fine-grained permissions, repository selection, and short-lived tokens. An installation can be limited to selected repositories, and installation tokens expire after one hour:

Recommended minimum design:

  1. Register a pm-gpt GitHub App only when implementation is approved.
  2. Request the smallest repository and organization permissions needed for the selected feature set.
  3. Let users choose specific repositories during installation.
  4. Store the installation ID and encrypted grant metadata, not long-lived installation tokens.
  5. Mint short-lived, repository-restricted tokens just in time.
  6. Verify webhook signatures, installation/tenant binding, replay window, delivery ID, and event type.
  7. Queue sync rather than performing repository work inside the webhook request.
  8. Reconcile uninstall, suspension, repository removal, permission changes, and token failures immediately.

Installation and user authorization are separate in GitHub. Use installation tokens for service-owned sync and user-to-server authorization only when the action must be attributed to a user and the intersection of user and app permissions is required.

Provider adapter contract

Every connected service should implement the same conceptual interface:

authorize() / revoke()
listContainers()
bindWorkspace()
pullSnapshot() / pushSnapshot()
subscribe() / verifyWebhook()
getCapabilities()
getSyncStatus()
resolveConflict()

Each adapter declares:

  • read/write/open-world behavior;
  • credential type and expiry;
  • supported data mapping;
  • rate limits and retry semantics;
  • source-of-truth direction;
  • conflict policy;
  • deletion and revocation behavior; and
  • audit attribution.

GitLab and Gitea can follow the repository adapter. Google Drive can support explicit archive import/export or document attachments; it should not be presented as a repository clone unless the mapping is complete. Generic file upload/download remains the universal provider-independent path.

Sync policy

Default to manual import/export or explicit sync. Background bidirectional sync is a later feature because it requires conflict resolution and can produce writes the user did not directly request in the conversation.

For every binding, record:

  • managed workspace remains authoritative, provider remains authoritative, or explicit bidirectional mode;
  • last successfully observed provider revision;
  • last exported workspace version;
  • conflict state and resolution choice;
  • actor and initiating request; and
  • reversible checkpoint before overwrite.

MCP server contract

Transport

Use MCP Streamable HTTP at https://pm-gpt.unbrained.dev/mcp. The current Apps SDK quickstart uses the official Model Context Protocol TypeScript SDK, StreamableHTTPServerTransport, and POST/GET/DELETE handling on /mcp. Prefer stateless request handling where practical and bind every request to explicit authenticated context.

Server instructions should be concise and self-contained; the first 512 characters are especially important. They should explain when to use pm-gpt, that a workspace must be selected, that GitHub is optional, and that writes or execution may need confirmation.

Tool design rules

Every public tool needs:

  • one user-visible job;
  • a precise verb/noun name and description;
  • a closed typed inputSchema where possible;
  • an exact outputSchema whenever structuredContent is returned;
  • stable machine IDs and bounded pagination;
  • accurate readOnlyHint, openWorldHint, and destructiveHint values;
  • idempotentHint only when the complete behavior is idempotent;
  • declared OAuth securitySchemes and required scopes;
  • server authorization independent of annotations;
  • deterministic conflict and retry errors; and
  • no private-only payload in model-visible content.

The official Apps SDK currently treats omitted or null safety annotations as validation errors. Tool metadata is part of the security and confirmation UX, not optional decoration.

Proposed capability tiers

Tier Capability Initial release posture
A Workspace selection and bounded reads Ship first
B Reversible pm writes Ship after OAuth, tenancy, idempotency, and confirmation proof
C Consequential lifecycle/admin writes Dedicated tools, stronger confirmation and scopes
D External provider reads/writes Dedicated integration tools with openWorldHint: true
E Tests, packages, extensions, subprocesses Isolated asynchronous jobs only; later gate

Proposed focused tool surface

Names are planning names, not a frozen API. The implementation must derive exact schemas from stable SDK contracts and run a naming review before publication.

Domain Proposed tools Safety posture
Workspace reads pm_list_workspaces, pm_get_workspace, pm_get_workspace_status Read-only, closed world
Workspace lifecycle pm_create_workspace, pm_import_workspace, pm_export_workspace Writes; export is read-only but may create a temporary artifact
Workspace deletion pm_delete_workspace Destructive, explicit high-friction confirmation
Orientation pm_get_context, pm_get_next_work Read-only, closed world
Item reads pm_search_items, pm_list_items, pm_get_item Read-only, paginated
Item creation pm_create_item, pm_copy_item Write, idempotency key required
Item edits pm_update_item, pm_append_item_body Write, expected version required
Ownership pm_claim_item, pm_release_item Write, principal-bound author
Lifecycle pm_close_item, later pm_cancel_item, pm_restore_item Consequential; split by behavior
Comments pm_list_comments, pm_add_comment, later edit/delete tools Split reads and mutations
Notes/learnings Separate list/add/edit/remove tools per record type Split reads and mutations
Links/docs/files Separate list/link/unlink or upload/download tools Never combine list, add, remove, and run
Dependencies pm_get_dependency_graph, pm_add_dependency, pm_remove_dependency Graph read separate from writes
Plans Separate get/create/step/update/approve/materialize tools Approval and materialization are consequential
History pm_get_item_history, pm_get_activity Read-only, bounded
Governance pm_validate_workspace, pm_get_health, pm_get_contracts Check-only; no repair side effects
Schema/config/profile Separate show/list tools from mutate/apply tools Admin scopes for mutations
Statistics pm_get_workspace_stats, bounded aggregates Read-only
Tests/jobs pm_start_test_job, pm_get_job, pm_get_job_logs, pm_cancel_job Isolated, asynchronous, scoped
Packages/extensions Dedicated inspect/install/upgrade/remove job tools Open-world and potentially destructive
Integrations list/connect/status/sync/disconnect per provider Open-world, explicit provider scopes

The existing local MCP server exposes narrow tools plus pm_run. pm_run is useful for trusted local operator coverage, but it is unsuitable for the public app because one annotation and one permission set cannot truthfully describe all of its actions.

Result envelope

Use a consistent output envelope where it improves client reliability:

{
  "workspace": {
    "id": "ws_opaque",
    "version": 42
  },
  "result": {},
  "warnings": [],
  "next_actions": [],
  "request_id": "req_opaque"
}

The exact outputSchema must describe every field. Put model-usable data in structuredContent, optionally add a short human summary in content, and reserve _meta for widget-only values. Do not place bearer tokens, provider credentials, internal filesystem paths, unredacted logs, tenant database keys, or private debugging state in any model-visible field.

Error contract

Return stable typed errors for:

  • authentication required;
  • insufficient scope or role;
  • workspace not selected or inaccessible;
  • stale workspace/item version;
  • idempotency-key conflict;
  • item lifecycle conflict;
  • active claim conflict;
  • invalid custom schema field;
  • import incompatibility;
  • provider grant expired or revoked;
  • job quota or sandbox policy refusal; and
  • transient retryable dependency failure.

Authentication failures that should launch the ChatGPT OAuth UI need the documented _meta["mcp/www_authenticate"] challenge and standards-compliant WWW-Authenticate behavior.

UI and state management

Start with tools as the complete functional API. Add an MCP Apps widget because it materially improves:

  • workspace selection and creation;
  • item tables, boards, dependency graphs, timelines, and plan review;
  • diff/confirmation for consequential mutations;
  • provider installation and sync status;
  • import/export progress;
  • job progress and bounded logs; and
  • conflict resolution.

Use the open MCP Apps bridge and _meta.ui.resourceUri as the portable foundation. Use ChatGPT-specific window.openai APIs only for optional enhancements. Return data independently of rendering so every tool remains usable by Codex and non-widget clients.

The backend is authoritative for business data and cross-session state. Widget state is ephemeral presentation state. The widget must tolerate remount, restore from server data, and never make an unconfirmed mutation merely because it rendered.

If a UI is submitted:

  • serve it as text/html;profile=mcp-app through an MCP resource;
  • use a dedicated component origin;
  • declare exact CSP domains in _meta.ui.csp;
  • keep the CSP minimal and avoid wildcard network access;
  • support keyboard use, focus, reduced motion, high contrast, dark mode, resize, and mobile;
  • sanitize all model/provider/user content before rendering; and
  • separate data-fetch tools from render tools when that improves tool selection.

Authentication and authorization

OAuth requirement

Private workspace reads and all writes require OAuth 2.1 compatible with the MCP authorization specification. The resource server must publish protected-resource metadata at:

https://pm-gpt.unbrained.dev/.well-known/oauth-protected-resource

That metadata points at the authorization server. The authorization server must provide OAuth/OIDC discovery and support a ChatGPT-compatible client onboarding path. Current OpenAI documentation supports Client ID Metadata Documents, Dynamic Client Registration where needed, or a predefined client, plus authorization code with PKCE S256.

The server must validate on every request:

  • token signature and algorithm policy;
  • issuer;
  • audience/resource equal to the canonical pm-gpt MCP resource;
  • expiry and not-before time with bounded skew;
  • required scopes;
  • tenant membership and workspace role;
  • revocation, disabled user, and service policy; and
  • optional client/mTLS binding where configured.

Echo the OAuth resource parameter into the token audience according to the MCP authorization requirements. Reject tokens minted only for the Authentik UI or another audience.

Proposed scope model

pm.workspace.read
pm.workspace.write
pm.workspace.admin
pm.items.read
pm.items.write
pm.history.read
pm.schema.read
pm.schema.write
pm.execution.run
pm.integrations.read
pm.integrations.github
pm.integrations.write
offline_access                 # only when durable refresh is actually needed

Avoid a single pm:* scope. Tools should declare the smallest useful scope, while the server also evaluates workspace role and per-operation policy. High-risk scopes should not be requested until the user invokes the corresponding feature.

Authentik feasibility

The existing deployment at https://auth.unbrained.dev is healthy and currently runs Authentik 2026.5.2. Official Authentik documentation confirms support for generic OAuth2/OIDC providers, authorization code, PKCE, scopes, JWKS, discovery, public/confidential clients, and refresh tokens through offline_access:

The research did not establish that this deployment directly supports every MCP-specific requirement:

  • Client ID Metadata Documents;
  • Dynamic Client Registration;
  • RFC 9728 protected-resource metadata;
  • preservation of the OAuth resource parameter; or
  • canonical pm-gpt resource audience issuance.

Recommendation:

  1. Let pm-gpt itself serve protected-resource metadata.
  2. First prove a predefined ChatGPT client with PKCE against a non-production Authentik provider.
  3. Add the pm-gpt audience and scope mappings and verify real tokens, not configuration screenshots.
  4. Test consent, refresh, logout/revocation, disabled users, key rotation, and scope escalation.
  5. If the required audience/client behavior cannot be implemented cleanly, use an established MCP-compatible identity provider or a narrowly reviewed compatibility layer. Do not build an ad hoc OAuth server.

Existing GitHub and Google social sources and production email delivery are separate Authentik prerequisites; they are not currently proven for pm-gpt. They may improve sign-in convenience later but cannot make GitHub a required identity.

Account linking

Keep identities separate:

  • pm-gpt principal identifies the user in pm-gpt;
  • ChatGPT OAuth client identifies the client authorization;
  • GitHub installation identifies repository access;
  • optional GitHub user authorization identifies user-attributed GitHub actions.

Link them through explicit, revocable records. Never merge accounts solely on an unverified email string.

Caddy and Cloudflare deployment plan

Live state on 2026-07-11

The Caddy deployment was inspected read-only on its canonical host 192.168.1.183 in /home/steve/caddy.

  • Caddy, Caddy Stats, CrowdSec, and DuckDNS containers were healthy.
  • pm-gpt.unbrained.dev resolves to Cloudflare proxy A and AAAA addresses.
  • The hostname is absent from the Caddy source catalog and active route files.
  • A public request returns Cloudflare 525 because no origin route/certificate currently serves the hostname.
  • A local SNI probe also fails before a site block exists.

This is the expected pre-implementation state. Do not add the route until a real upstream, /mcp, health, OAuth metadata, legal/support endpoints, and OpenAI domain-challenge handler are ready.

Correct future source-of-truth workflow

The canonical Caddy project generates host catalog outputs from npm_proxy_hosts_extracted.json; generated files must not be hand-edited. Because pm-gpt has streaming MCP, OAuth discovery, challenge, legal, and no-cache requirements, use a custom route while still registering the host in the source catalog so dashboards and generation remain accurate.

Future implementation sequence:

  1. Decide the internal upstream service name and port after checking collisions.
  2. Prefer a Docker service alias such as pm-gpt on the shared Caddy network rather than a public host port.
  3. Add the hostname to npm_proxy_hosts_extracted.json with a custom-file next action.
  4. Add a reviewed custom route under the canonical enabled-site import path.
  5. Regenerate derived catalog outputs with the repository workflow.
  6. Validate the full Caddy config before reload.
  7. Start the upstream and prove its internal health before public reload.
  8. Reload Caddy, then test local SNI, public HTTPS, MCP initialize/list/call, OAuth discovery/challenge, and streaming/reconnect behavior.
  9. Only enable Authenticated Origin Pulls after the Cloudflare side is enabled; preserve that order.

Illustrative route shape

This is a design sketch, not an applied configuration. The real upstream and exact snippet names must be confirmed against the then-current Caddy checkout.

pm-gpt.unbrained.dev {
    import default_proxy
    import common_json_access_log pm-gpt.unbrained.dev

    @dynamic path \
        /mcp \
        /mcp/* \
        /.well-known/oauth-protected-resource \
        /.well-known/openai-apps-challenge \
        /healthz

    handle @dynamic {
        header Cache-Control "no-store"
        reverse_proxy pm-gpt:<internal-port> {
            health_uri /healthz
        }
    }

    handle {
        reverse_proxy pm-gpt:<internal-port>
    }
}

Do not copy the placeholder into production. Confirm Caddy directive order after adaptation. Legal, support, privacy, terms, and static app assets may use a different cache policy from MCP and OAuth endpoints.

Caddy's reverse proxy recognizes streaming responses and flushes them promptly. Avoid response buffering. Set a negative flush_interval only if end-to-end tests prove additional low-latency behavior is needed; it also changes disconnect handling. See Caddy reverse_proxy streaming.

Cloudflare requirements

Create a Cache Rule matching pm-gpt.unbrained.dev that bypasses cache for MCP, OAuth, API, challenge, health, and authenticated UI traffic. Origin Cache-Control: no-store remains defense in depth. Cloudflare documents the bypass setting in Cache Rules.

Cloudflare currently documents a 120-second proxy read timeout for most zones and a 30-second proxy write timeout. Keep individual non-streaming work bounded, emit valid streaming activity where the protocol expects it, and move long operations into asynchronous jobs. See Cloudflare connection limits.

Also verify:

  • Full (strict) origin TLS after Caddy obtains the certificate;
  • Web Application Firewall and rate-limit behavior for POST/GET/DELETE /mcp;
  • no caching or transformations of OAuth and MCP responses;
  • upload limits for workspace archives and attachments;
  • webhook paths and provider IP/signature policy;
  • request IDs across Cloudflare, Caddy, and the app; and
  • controlled behavior during Caddy reload and upstream deployment.

Logging and secrets

The current Caddy logging snippets redact cookies, authorization headers, set-cookie values, and common secret query parameters. Preserve those snippets. The app must independently avoid logging:

  • raw OAuth tokens and authorization codes;
  • provider tokens and webhook secrets;
  • raw prompts unless strictly needed and consented;
  • imported file contents;
  • full tool arguments that may contain personal data; and
  • unbounded test/job output.

Security and privacy threat model

Treat prompt injection as normal hostile input. Repository text, pm item bodies, comments, docs, provider content, and uploaded files can all contain instructions aimed at the model or tool service.

Threat Required controls
Cross-tenant access Opaque tenant/workspace keys, server-side membership checks, row/storage isolation, negative tests
Item-ID guessing Never authorize by pm-* ID alone; require resolved workspace and role
Path traversal/symlink escape Canonicalize paths, contain roots, reject absolute/parent/symlink escapes
Prompt injection Treat content as data, never translate returned instructions into privileged tool calls automatically
Duplicate model retries Idempotency keys and stored results around every non-idempotent mutation
Confused deputy Bind token audience/client/principal/workspace; require operation scopes and roles
SSRF/open egress URL allowlists, DNS/IP validation, blocked metadata/private ranges, sandbox egress policy
Provider token theft Envelope encryption, KMS/secret store, short-lived tokens, rotation, no model exposure
Malicious archive Size/file-count limits, decompression ratio limit, path validation, isolated extraction
Malicious Git repository Shallow/limited materialization, no hooks, safe config, isolated worker, bounded objects
Package/extension supply chain Allowlist/signature/integrity policy, no install in API, disposable worker, no lifecycle surprise
Arbitrary test execution Dedicated sandbox, CPU/memory/time/process quotas, default-deny network, explicit confirmation
Output exfiltration Bounded results, redaction, content classification, no secret-bearing _meta to the model
Webhook spoof/replay Provider signature, timestamp/delivery replay cache, installation binding
Stale authorization Revocation hooks, short token TTL, policy check per request, disabled-user handling
Destructive actions Dedicated tool, explicit confirmation, backup/checkpoint, admin scope, audit trail

Worker sandbox minimum

Before Tier E capabilities are enabled, workers need:

  • one job and one workspace materialization per disposable sandbox;
  • no Docker socket or host filesystem access;
  • read-only base image and minimal writable scratch space;
  • non-root user, dropped capabilities, seccomp/AppArmor or equivalent;
  • CPU, memory, storage, process, wall-clock, and output limits;
  • default-deny network with per-operation allowlists;
  • injected short-lived secrets scoped to one job;
  • cancellation and hard termination;
  • immutable job request and audit record; and
  • destruction of scratch state after evidence is persisted.

Never load a customer-provided pm extension inside the long-lived API process.

Data lifecycle

Define before beta:

  • data categories and purposes;
  • regional storage and subprocessors;
  • default retention for workspace data, exports, audit, logs, and job artifacts;
  • backup retention and encrypted restore workflow;
  • account/workspace export format;
  • soft-delete window and final deletion timing;
  • legal hold policy if applicable;
  • provider revocation behavior;
  • support-access controls and audit; and
  • incident notification and credential-rotation procedures.

Privacy policy and terms must describe the actual hosted service, not link to the pm-cli open-source license or security file as a substitute.

SDK and full-feature parity

The hosted backend should call the public pm SDK, not import private src/core files and not spawn the CLI for ordinary business actions. Upstream pm-usfg is promoting all CLI domains into stable SDK primitives. As of this research pass, important open domains include annotations/links, schema/config/profile/init, governance and health, history, plan workflows, and execution/diagnostics.

Consequences:

  1. Implement initial tools only from stable public SDK contracts.
  2. A temporary transport-neutral action adapter may use a supported SDK runAction internally, but public tool schemas must remain focused and must not expose its arbitrary action surface.
  3. Track upstream SDK completion before claiming typed parity.
  4. Version the pm-gpt server, plugin, SDK compatibility range, archive format, and worker image together.
  5. Run compatibility tests against the oldest and newest supported pm archive/SDK versions.

"Every pm feature" is a valid end-state requirement, not a safe v1 promise. Full parity is complete only when every capability is mapped to one of:

  • a safe focused synchronous tool;
  • an authenticated administrative tool;
  • an isolated asynchronous job;
  • an optional provider integration; or
  • an explicitly local-only capability with a documented reason and approved product exception.

The last category should be empty for the final parity milestone unless the maintainers deliberately revise the requirement.

Private repository proposal

The remote and local repository are currently empty by design. When implementation is approved, use a structure similar to:

pm-gpt/
├── .codex-plugin/
│   └── plugin.json               # only if plugin root is the repository root
├── docs/
│   ├── architecture/
│   ├── security/
│   ├── operations/
│   └── legal/
├── packages/
│   ├── server/
│   ├── mcp-contracts/
│   ├── workspace-service/
│   ├── integration-service/
│   ├── job-service/
│   ├── widget/
│   └── plugin/
├── workers/
│   └── pm-runner/
├── infra/
│   ├── compose/
│   └── migrations/
└── tests/
    ├── contract/
    ├── integration/
    ├── security/
    └── e2e/

The exact layout is an ADR detail. Important boundaries are:

  • transport-neutral pm action contracts;
  • server-only backend code and secrets;
  • a separately buildable, inspectable public plugin artifact;
  • a separately sandboxed worker image; and
  • migrations and deployment assets reviewed independently from application code.

Do not copy the open-source plugins/pm-codex tree wholesale. Reuse concepts and stable SDK packages while preserving license notices, but build the hosted client package around the actual app ID and hosted contracts.

Plugin package plan

Future plugin.json needs real production values for:

  • kebab-case name, strict semver, description, author, homepage, repository/support policy, and license terms;
  • apps: "./.app.json" only when the actual app mapping exists;
  • mcpServers only if a second direct Codex MCP registration is intentionally required and tested;
  • display name, descriptions, developer, category, and accurate capabilities;
  • website, privacy, and terms HTTPS URLs;
  • at most three concise starter prompts;
  • production brand color, composer icon, light/dark logos, and screenshots; and
  • skills whose icon paths and instructions validate inside the packaged archive.

Recommended initial package composition:

pm-gpt-plugin/
├── .codex-plugin/plugin.json
├── .app.json
├── skills/
│   ├── pm-workspace/
│   └── pm-planning/
└── assets/

Avoid .mcp.json initially if .app.json gives every target surface the hosted app. Add it only after proving that a supported Codex surface requires a separate remote MCP entry and that doing so does not duplicate tools or authentication prompts.

Before any release:

  1. Generate or update through the current @plugin-creator.
  2. Run its validator against the assembled archive.
  3. Install from the real distribution source into a fresh isolated Codex home.
  4. Start a new task and prove skills, app, OAuth, and tools.
  5. Inspect the cached package to ensure no source-repository-relative dependency or secret exists.
  6. Prove update/cache-busting behavior with a real version increment.

Phased implementation plan

No phase below has started.

Phase 0 — approve architecture

Gate: companion Decision pm-cli-website-wsp9.

  • Approve authoritative storage and export format.
  • Approve Authentik proof path or alternate identity provider.
  • Approve tenant, role, scope, and provider identity model.
  • Approve synchronous versus sandboxed operation map.
  • Approve initial tool tier and full-parity definition.
  • Decide plugin relationship to pm-codex.
  • Approve retention, deletion, backup, and incident assumptions.

Phase 1 — repository and service foundations

  • Add private repository governance, secret scanning, dependency policy, branch protection, CI, and release provenance.
  • Define threat model and data inventory before schemas.
  • Establish transport-neutral MCP contracts and generated schemas.
  • Establish tenant/workspace database and isolated durable storage in a non-production environment.
  • Add request IDs, structured redacted logs, metrics, traces, and health/readiness.
  • Implement only workspace create/list/get and read-only pm orientation through the public SDK.

Exit gate: no cross-tenant finding, export round-trip proof, bounded outputs, and no subprocess in the API.

Phase 2 — OAuth and reversible writes

  • Serve protected-resource metadata.
  • Prove Authentik or selected provider with real ChatGPT developer mode.
  • Implement scopes, roles, consent, revocation, refresh policy, and account linking.
  • Add idempotency store and optimistic workspace versions.
  • Add focused item create/update/comment/claim/release tools.
  • Add explicit confirmation behavior and audit attribution.

Exit gate: auth negative matrix, retry/idempotency proof, disabled/revoked-user proof, and cross-tenant suite.

Phase 3 — widget and managed-workspace experience

  • Add project selector, item views, plan/dependency visualization, and mutation review.
  • Implement server-authoritative state restore.
  • Add import/export and attachment flows.
  • Complete accessibility, CSP, origin, responsive, and mobile testing.

Exit gate: every workflow remains functional without the widget and no widget-only authority exists.

Phase 4 — optional provider integrations

  • Register the approved GitHub App.
  • Implement selected-repository installation, short-lived tokens, webhook verification, and revocation.
  • Start with manual import/export or one-way sync.
  • Add conflict checkpoints before bidirectional sync.
  • Implement the provider adapter contract and at least one non-GitHub path or universal archive proof.

Exit gate: a no-GitHub user passes the same core acceptance suite as a GitHub-connected user.

Phase 5 — isolated execution and full parity

  • Build disposable worker image and default-deny policies.
  • Add asynchronous job model, quotas, cancellation, bounded logs, and artifact retention.
  • Map tests, validation repair, packages, extensions, Git, history maintenance, and other process operations to dedicated job tools.
  • Complete upstream SDK dependency review.
  • Prove every CLI/SDK capability against the parity inventory.

Exit gate: sandbox escape, egress, resource exhaustion, malicious archive/repository, supply-chain, and secret exfiltration suites pass.

Phase 6 — production edge and operations

  • Deploy a production-ready upstream on the Caddy network.
  • Add the source-catalog record and reviewed custom Caddy route.
  • Add Cloudflare cache bypass and security/rate rules.
  • Prove TLS, MCP streaming, OAuth, domain challenge, health, reconnect, deployment, backup, and restore.
  • Complete SLOs, alerts, on-call/runbooks, capacity, cost, abuse, and incident response.

Exit gate: restore drill and controlled failure/deployment exercises pass.

Phase 7 — plugin and OpenAI submission

  • Create the real developer-mode app and .app.json mapping.
  • Build and validate the thin plugin artifact.
  • Finish support, privacy, terms, branding, starter prompts, regions, and reviewer account.
  • Verify the domain challenge at /.well-known/openai-apps-challenge.
  • Run portal scan and remediate every finding.
  • Prepare exactly five positive and three negative reviewer cases.
  • Submit, pass review, and explicitly publish only the verified production artifact.

Exit gate: production head, plugin archive, tool descriptors, legal text, and reviewer evidence all match.

Verification strategy

Handler and contract tests

  • schema acceptance/rejection and unknown-key behavior;
  • output conformance for every successful and error result;
  • empty, pagination, maximum-size, and Unicode cases;
  • retries and idempotency replay/conflict;
  • stale item/workspace version and claim conflicts;
  • lifecycle, custom schema, and role enforcement; and
  • no secret/private field in content, structuredContent, or _meta.

Auth tests

  • missing/malformed/expired/not-yet-valid token;
  • wrong signature, issuer, audience/resource, scope, client, tenant, or role;
  • PKCE downgrade/reuse and redirect URI mismatch;
  • refresh rotation/reuse, logout, revocation, disabled user, and key rotation;
  • predefined client and any later CIMD/DCR path;
  • consent escalation and reduced-scope token; and
  • OAuth UI launch through the documented MCP challenge.

Multi-tenant and security tests

  • substitute every workspace/item/provider/job identifier across tenants;
  • path traversal, symlink, archive traversal, decompression bomb, and oversized input;
  • prompt injection in every text-bearing entity;
  • SSRF, DNS rebinding, metadata IP, and redirect-chain tests;
  • webhook signature, replay, wrong installation, and stale delivery;
  • job sandbox escape, network egress, fork bomb, disk fill, secret lookup, and log flooding; and
  • restore/delete/export authorization and audit.

MCP and OpenAI client tests

  • MCP Inspector initialize, list, call, errors, resources, and UI;
  • ChatGPT developer mode direct, indirect, ambiguous, and negative prompts;
  • API Playground raw protocol and auth inspection;
  • correct tool choice with multiple workspaces and integrations;
  • read/write/destructive confirmation alignment;
  • Codex CLI, Codex IDE, ChatGPT web, desktop, and mobile where supported;
  • app refresh, plugin update, logout/reconnect, and new-task behavior; and
  • component CSP, origin, theme, resize, keyboard, screen reader, and state restore.

Edge and operational tests

  • internal health before Caddy route activation;
  • Caddy validate/reload and local SNI probe;
  • public TLS, HTTP versions, /mcp, OAuth well-known, challenge, legal/support, and cache headers;
  • Cloudflare cache status, WAF/rate behavior, upload limits, and 120-second boundary behavior;
  • streaming heartbeat/reconnect across Caddy reload and application deployment;
  • upstream failure, database/object-store/job-queue degradation, and retry behavior;
  • backup integrity and isolated restore drill; and
  • request ID correlation without credential leakage.

Required OpenAI review evidence

The production review package needs:

  • public production MCP URL;
  • verified developer/business identity and submitting organization permissions;
  • exact auth instructions and reviewer credentials without MFA, email, SMS, or private-network dependence;
  • privacy, terms, website, support, logo, category, descriptions, prompts, and regional availability;
  • exact UI CSP and verified domains;
  • five positive cases showing intended value; and
  • three negative cases showing when the plugin must not invoke or must refuse.

Approval does not publish automatically. Recheck production after approval, then publish deliberately.

Observability and service objectives

Define targets before beta rather than after an incident. At minimum measure:

  • MCP initialize/list/call count, latency, result, and tool name;
  • OAuth challenge, callback, token validation, refresh, and failure class;
  • workspace operation latency, lock contention, version conflict, and idempotency replay;
  • worker queue delay, run time, cancellation, quota rejection, and sandbox termination reason;
  • provider API latency, rate limit, webhook lag, sync revision, and conflict;
  • Caddy/Cloudflare status and upstream health;
  • storage capacity, backup age, restore verification, and deletion backlog; and
  • per-tenant abuse/cost signals without raw-content logging.

Suggested initial objectives to approve, not promises:

  • 99.9% monthly availability for authenticated core MCP reads/writes after GA;
  • p95 under two seconds for non-search core reads at normal workspace sizes;
  • no synchronous request dependent on a job longer than the Cloudflare read-timeout boundary;
  • recovery point and recovery time objectives derived from documented backup frequency and restore drills; and
  • zero tolerance for cross-tenant data disclosure.

pm CLI execution and tracking model

The companion tracker is the system of record for this private service. The official pm-cli tracker remains the system of record only for reusable SDK/plugin fixes accepted into that product.

Current companion items:

Item State Purpose
pm-cli-website-0fx3 Open, unclaimed Hosted product feature and final acceptance criteria
pm-cli-website-hmyb Research task This dated evidence and documentation pass
pm-cli-website-wsp9 Open, unclaimed Mandatory architecture approval gate

After approval, create narrowly scoped children under pm-cli-website-0fx3. Recommended types:

  1. Feature — private repository/service foundation and managed workspace.
  2. Feature — OAuth and tenant authorization.
  3. Feature — focused MCP read/write contract.
  4. Feature — MCP Apps widget.
  5. Feature — GitHub App and provider adapters.
  6. Feature — isolated job plane and full parity.
  7. Task — Caddy/Cloudflare production route and verification.
  8. Task — OpenAI developer-mode, security, submission, and publication.
  9. Task — privacy, terms, retention, support, and reviewer preparation.

For each implementation child:

pm context --limit 10
pm search "<exact scope>" --limit 20
pm create --create-mode progressive --parent pm-cli-website-0fx3 ...
pm claim <id>
pm update <id> --status in_progress --message "Start approved implementation"
pm files <id> --add path=<path>,scope=project,note="<why>"
pm docs <id> --add path=<path>,scope=project,note="<why>"
pm test <id> --add command="<safe command>",scope=project,timeout_seconds=<n>
pm comments <id> "Evidence: <exact proof>"
pm close <id> "<acceptance and verification summary>" --validate-close warn
pm release <id>

Create a separate Caddy implementation item only when the upstream service is ready. Until then, the 525 is known pre-route evidence, not an incident to remediate.

Open decisions

Implementation is blocked by approval, not by missing research. Decision pm-cli-website-wsp9 must resolve:

  1. Is the canonical workspace an encrypted filesystem tree plus database index, or a new storage adapter with filesystem export?
  2. What is the exact portable archive format and compatibility guarantee?
  3. Can Authentik issue and validate the required MCP resource audience through a predefined ChatGPT client?
  4. What account registration and recovery methods ship without GitHub or Google?
  5. Which Tier A/B/C tools form the first public release?
  6. Which features require Tier E sandbox jobs, and what is the sandbox technology?
  7. Does v1 include any background provider sync or only explicit import/export?
  8. Does pm-gpt ship its own skills-only client package, or later converge with pm-codex?
  9. What are the production retention, backup, restore, region, support, abuse, and SLO commitments?
  10. What upstream SDK milestone is required before the service may claim full parity?

Definition of done

Do not describe pm-gpt as complete or fully native until all selected claims have production evidence:

  • No-GitHub: a new user without a source-control account completes registration, workspace creation, core pm lifecycle, export, deletion, and reconnection.
  • Native app: ChatGPT and supported Codex surfaces discover and call the hosted MCP app with correct OAuth, schemas, annotations, outputs, confirmations, and optional UI.
  • Full pm: every CLI/SDK capability is mapped to a safe tool or isolated job and passes the parity inventory.
  • Optional GitHub: selected-repository GitHub App integration works, revokes cleanly, and never changes the no-GitHub acceptance path.
  • Secure tenancy: cross-tenant, path, injection, provider, token, and sandbox suites pass.
  • Operable service: Caddy/Cloudflare, logs, metrics, alerts, backup, restore, deletion, and incidents have tested runbooks.
  • Public plugin: current validator, client smokes, legal/listing material, portal scan, eight review cases, review approval, and deliberate publication all match the production artifact.
  • Private implementation: backend source and secrets remain in the private repository while the distributed plugin contains only the minimum public client material.

Until those gates pass, the accurate description is:

pm-gpt is a publicly available hosted service with a private, closed-source implementation repository and protected tenant data. Its GitHub-optional managed-workspace runtime is deployed; unrestricted production readiness remains gated by the approvals and capacity evidence listed above.


Pm Gpt Research And Implementation Plan local
Report an issue