An independently maintained, Apache-2.0 licensed SDK for building and validating data-only ScriptPlayer+ script-source packages. It is an independent public implementation of the JSON contract; it is not a copy of, or a license for, the ScriptPlayer+ application.
The SDK supports one API contract:
apiVersion:1engine:discourse-script-source-v1- package extension:
.funplugin
Conformance: the published JSON Schema is an editor and object-shape aid. It cannot enforce normalized equality between manifest fields or inspect PNG bytes. Only
validateManifestorfunplugin validatemakes a complete v1 conformance decision.
Packages declare one public HTTPS Discourse origin. The host supplies the adapter; a package supplies no code. A v1 package may request exactly these three capabilities, once each:
authenticationsearchscript-download
Direct downloads are limited to same-origin HTTPS URLs ending in
.funscript. External links, video links, archive links, and arbitrary asset
opening are outside this contract. JavaScript, WebAssembly, native libraries,
and any other executable plugin modules are never supported.
Requires Node.js 20 or newer. The npm packages are not assumed to be published yet. After cloning this repository, use the workspace command:
npm ci
npm run funplugin -- init my-source --id example.community --origin https://community.example.com --name "Example Community" --publisher-name "Community Maintainer" --publisher-url https://maintainer.example.org/
npm run funplugin -- validate .\my-source\manifest.json
npm run funplugin -- build --manifest .\my-source\manifest.json --out .\my-source\dist\example.community.funplugin
npm run funplugin -- test .\my-source\dist\example.community.funpluginEach workspace invocation builds the local SDK and CLI first. Once the CLI is
published and installed, the shorter funplugin ... form is equivalent.
test is a read-only provider conformance probe. It checks public DNS and the
expected JSON endpoints. Its default Node.js transport validates the resolved
public addresses, pins socket lookup to those addresses, confines redirects to
the manifest origin, and keeps its deadline active through body completion.
It is not a substitute for the host's network and installation controls.
The npm packages are not published yet, so these prompts tell the agent to use
this repository's workspace CLI. Open the SDK clone as the agent's workspace,
replace every [PLACEHOLDER], and use a new empty [PLUGIN_DIRECTORY] such as
work/example.community.
Never put passwords, session cookies, access tokens, or private signing keys in an AI prompt, manifest, issue, or repository. Confirm that you are allowed to access the provider and distribute the scripts you intend to use.
Paste this into Codex while the SDK repository is open:
Create one unsigned ScriptPlayer+ script-source package using this repository as the authoritative SDK and contract.
Inputs:
- plugin directory: [PLUGIN_DIRECTORY]
- plugin ID: [PLUGIN_ID]
- public Discourse origin: [PUBLIC_DISCOURSE_ORIGIN]
- display name: [DISPLAY_NAME]
- publisher name: [PUBLISHER_NAME]
- publisher public HTTPS URL: [PUBLISHER_URL]
- optional search query suffix: [SEARCH_QUERY_SUFFIX_OR_NONE]
- provider test query: [TEST_QUERY]
Before changing anything, read README.md, docs/SPEC.md, docs/CLI.md, docs/THREAT_MODEL.md, and docs/PUBLISHERS.md. Follow any AGENTS.md files that apply. Treat the checked-out repository as authoritative; the npm packages are not assumed to be published.
If any required value above is still a bracketed placeholder, stop and ask me for it before running `init`.
Use Node.js 20 or newer and run `npm ci` at the SDK root. Preserve unrelated work and do not modify the SDK, schema, validator, probe, or security limits just to make a package pass. Create the package only in the new empty [PLUGIN_DIRECTORY].
From the SDK root, initialize it with:
`npm run funplugin -- init "[PLUGIN_DIRECTORY]" --id "[PLUGIN_ID]" --origin "[PUBLIC_DISCOURSE_ORIGIN]" --name "[DISPLAY_NAME]" --publisher-name "[PUBLISHER_NAME]" --publisher-url "[PUBLISHER_URL]"`
Review the generated manifest. If [SEARCH_QUERY_SUFFIX_OR_NONE] is `NONE`, delete `metadata.searchQuerySuffix`; otherwise replace it with the supplied value. Keep API v1, engine `discourse-script-source-v1`, and exactly the `authentication`, `search`, and `script-download` capabilities. The origin must be one public HTTPS Discourse origin. Permit only same-origin direct `.funscript` downloads. Do not add video, external, archive, or arbitrary asset links; executable JavaScript, WebAssembly, or native modules; credentials; bundled scripts; or authentication bypasses.
Run all of these checks and fix package errors without weakening the contract:
1. `npm run funplugin -- validate "[PLUGIN_DIRECTORY]/manifest.json" --json`
2. `npm run funplugin -- build --manifest "[PLUGIN_DIRECTORY]/manifest.json" --out "[PLUGIN_DIRECTORY]/dist/[PLUGIN_ID].funplugin" --force --json`
3. `npm run funplugin -- validate "[PLUGIN_DIRECTORY]/dist/[PLUGIN_ID].funplugin" --json`
4. `npm run funplugin -- test "[PLUGIN_DIRECTORY]/dist/[PLUGIN_ID].funplugin" --query "[TEST_QUERY]" --json`
Do not claim the provider probe passed if network access, authentication, provider state, or missing same-origin `.funscript` data prevented verification. Do not sign, commit, push, publish, install into ScriptPlayer+, or create/store a private key unless I explicitly ask.
Finish with the changed-file list, every command and result, the artifact's absolute path and SHA-256, and any remaining manual or unverified checks.
Paste this into Claude Code while the SDK repository is open:
Work in this repository and create one unsigned ScriptPlayer+ script-source package with the values below.
- plugin directory: [PLUGIN_DIRECTORY]
- plugin ID: [PLUGIN_ID]
- public Discourse origin: [PUBLIC_DISCOURSE_ORIGIN]
- display name: [DISPLAY_NAME]
- publisher name: [PUBLISHER_NAME]
- publisher public HTTPS URL: [PUBLISHER_URL]
- optional search query suffix: [SEARCH_QUERY_SUFFIX_OR_NONE]
- provider test query: [TEST_QUERY]
Before editing, read @README.md, @docs/SPEC.md, @docs/CLI.md, @docs/THREAT_MODEL.md, and @docs/PUBLISHERS.md. Follow any loaded CLAUDE.md instructions. Treat those files as the authoritative contract; the npm packages are not assumed to be published.
If any required value above is still a bracketed placeholder, stop and ask me for it before running `init`.
Use Node.js 20 or newer and run `npm ci` at the SDK root. Preserve unrelated work and create the package only in the new empty [PLUGIN_DIRECTORY]. Do not modify or weaken the SDK, schema, validator, provider probe, DNS checks, redirect rules, size limits, or other security controls to make a package pass.
From the SDK root, run:
`npm run funplugin -- init "[PLUGIN_DIRECTORY]" --id "[PLUGIN_ID]" --origin "[PUBLIC_DISCOURSE_ORIGIN]" --name "[DISPLAY_NAME]" --publisher-name "[PUBLISHER_NAME]" --publisher-url "[PUBLISHER_URL]"`
Review the generated manifest. If [SEARCH_QUERY_SUFFIX_OR_NONE] is `NONE`, delete `metadata.searchQuerySuffix`; otherwise replace it with the supplied value. Keep API v1, engine `discourse-script-source-v1`, and exactly the `authentication`, `search`, and `script-download` capabilities. The origin must be one public HTTPS Discourse origin. Permit only same-origin direct `.funscript` downloads. Do not add video, external, archive, or arbitrary asset links; executable JavaScript, WebAssembly, or native modules; credentials; bundled scripts; or authentication bypasses.
Run each command below. If one fails, diagnose the root cause, fix package errors without weakening the contract, and run it again:
1. `npm run funplugin -- validate "[PLUGIN_DIRECTORY]/manifest.json" --json`
2. `npm run funplugin -- build --manifest "[PLUGIN_DIRECTORY]/manifest.json" --out "[PLUGIN_DIRECTORY]/dist/[PLUGIN_ID].funplugin" --force --json`
3. `npm run funplugin -- validate "[PLUGIN_DIRECTORY]/dist/[PLUGIN_ID].funplugin" --json`
4. `npm run funplugin -- test "[PLUGIN_DIRECTORY]/dist/[PLUGIN_ID].funplugin" --query "[TEST_QUERY]" --json`
Do not claim the provider probe passed if network access, authentication, provider state, or missing same-origin `.funscript` data prevented verification. Do not sign, commit, push, publish, install into ScriptPlayer+, or create/store a private key unless I explicitly ask.
When finished, report the changed files, every verification command and its result, the built artifact's absolute path and SHA-256, and all remaining manual or unverified checks.
For reusable repository instructions, see the official guides for
Codex prompting,
Codex AGENTS.md,
Claude Code best practices, and
Claude Code memory.
Unsigned packages are the normal public starting point. Build the artifact, publish its SHA-256 alongside a changelog, and let users install it through the host's confirmation flow. An unsigned package can be structurally valid but does not carry publisher identity assurance.
An admitted publisher can sign an artifact with an Ed25519 key. The SDK can create and cryptographically verify those signatures, but it cannot decide whether any host release admits that publisher or key. Admission is a separate host policy. Details: publisher guide.
- Format specification
- CLI reference
- Versioning and compatibility
- Publisher guide
- Threat model
- Security reporting
- Contributing
This repository, its schemas, SDK packages, templates, and documentation are licensed under Apache-2.0. See LICENSE and NOTICE.
The ScriptPlayer+ application remains proprietary. It is not distributed by this repository, and this repository does not grant rights to its source code, branding, binaries, or services.