README
¶
Vessica Studio
A local-first, agent-driven presentation studio built on ordinary files.
Documentation · Install the CLI and Codex plugin · Web editor guide · Architecture · Vessica voice agent
Vessica Studio turns a directory of HTML slide fragments and Markdown
companions into a live, editable presentation. Use the vstd CLI to create,
serve, review, fork, export, and deploy decks. Use Codex or another coding
agent to work directly on the same transparent file format.
The result is a presentation workflow that is inspectable, diffable, forkable, and deployable with Git—without locking the deck inside a proprietary editor.
Contents
- Why Vessica Studio
- Documentation
- Installation
- Five-minute quickstart
- Use Vessica Studio with Codex
- How a studio is organized
- CLI manual
- Studio and player features
- Configuration
- Hosted deployment
- Development and contributing
Why Vessica Studio
- File-native authoring. Every slide is an HTML fragment paired with a Markdown companion containing intent, evidence, sources, visual direction, talk track, and edit history.
- Agent-ready workflows. Six canonical authoring workflows are embedded in
the
vstdbinary and shared across Codex and other agent runtimes. - Live editing. The studio watches files, rebuilds decks, reloads connected browsers, and supports direct canvas, companion, sticky-note, and voice edits.
- Editable output. Download PDF or PowerPoint from the player. PowerPoint export preserves supported text, shapes, chart labels, and images as editable objects instead of flattening every slide into one image.
- Reusable media. Generate images, ingest video, maintain a shared asset manifest, paste images onto slides, and sync large video files to S3-compatible storage.
- Source-aware creation. Attach PDFs, PowerPoint files, documents, spreadsheets, or images to a slide companion so an editing agent can inspect the original source. Source-backed redesigns can run a separate visual critic pass against the reference.
- Forkable decks. Create audience- or client-specific variants while retaining upstream provenance and detecting later parent changes.
- Organized and reusable work. Keep a personal one-level folder view of owned and shared presentations, then send one or many slides to another deck as editable copies or read-only linked snapshots.
- Presentation and audience modes. Present locally, publish a read-only audience surface, mint expiring deck links, and optionally enable chat, voice, SMS, email, and live audience pulse.
- Local-first, cloud-capable. Author without a hosted service, connect or clone a Cloud workspace, synchronize revisions, and publish a selected revision without managing Git credentials or branches.
- Single-team collaboration. Invite teammates by email, keep per-user private catalogs, share selected decks read-only, and fork shared work before editing it.
Installation
For the visual setup path, including Codex desktop and CLI plugin installation, see the installation guide.
Requirements
Prebuilt Linux/macOS binaries and SHA256SUMS are distributed with the
GitHub releases.
They retain immutable source revision metadata for release-build. Verify the
downloaded binary against the release checksums before running it. The Go
requirement below applies when building from source.
- Go 1.26 or newer; the module selects patched Go 1.26.9 automatically when needed
- Git, if you want version control or hosted content sync
Optional tools unlock additional features:
| Tool | Used for |
|---|---|
| Chrome or Chromium | PDF export, editable PowerPoint export, chart-text migration, and visual review |
| FFmpeg and FFprobe | Video normalization, metadata inspection, and poster extraction |
| LibreOffice and Poppler | Rendering attached PowerPoint/PDF sources for visual comparison |
| OpenAI API key | Image generation, Vessica voice, transcription, audience chat, and agent tools |
| Railway CLI | Hosted deployment and service management |
| S3-compatible bucket | Storage for video bytes that should not live in Git |
Install with Go
go install github.com/vessica-labs/vessica-studio/cmd/vstd@latest
vstd version
If vstd is not found after installation, add Go's binary directory to your
PATH:
export PATH="$(go env GOPATH)/bin:$PATH"
Build from source
git clone https://github.com/vessica-labs/vessica-studio.git
cd vessica-studio
make build
./vstd version
Use make install PREFIX="$HOME/.local" for a user-local installation, or
make go-install to install through Go.
Optional OpenAI setup
The core file, build, serve, and presentation workflows do not need an API key. To enable AI-backed features:
export OPENAI_API_KEY="sk-..."
vstd key check
For a key that does not remain in shell history or a repository, use a secret
manager and set openai.api_key_cmd in studio.yaml. A macOS Keychain example
is included under Configuration.
Five-minute quickstart
Create a content repository and its first deck:
vstd init my-studio
cd my-studio
vstd new product-story --title "Our Product Story"
vstd serve product-story
Optional Vessica Studio Cloud workflow
These additive commands require a compatible hosted native-client API. The private service integration is tracked in VES-74; fake-cloud tests do not imply the public hosted service is available yet.
Local authoring, builds, serving, export, and skill discovery do not require a Cloud account or network connection. To use a Cloud workspace, first sign in with the native device flow:
vstd cloud login
vstd cloud account
vstd cloud presentation list
login prints a verification URL and user code. Renewable session material is
stored in the operating system credential store; access tokens are kept only in
memory. If secure credential storage is unavailable, login fails instead of
writing a plaintext fallback. Concurrent native CLI processes serialize refresh
credential rotation with an OS file lock, released automatically if a process
exits. The per-endpoint lock in the user cache directory contains no credentials;
refresh credentials remain in the OS credential store.
From any Codex chat or terminal directory, open an existing presentation by ID,
exact title, or one unambiguous partial title. Without --root, the CLI creates
or reuses an account-scoped managed checkout and reconciles it with the latest
Cloud revision:
vstd cloud presentation open "Acme strategy" --json
vstd cloud presentation create --title "New client story" --json
Both commands return the local root used by the file-native authoring skills.
The list marks known view-only presentations, and open refuses them before
creating a checkout.
Agent edits should run through vstd worktree begin and vstd worktree finish
so concurrent visual-editor changes are reconciled before the result is saved to
Cloud. A shared viewing link by itself does not grant authoring permission.
Clone a workspace into a directory that does not yet exist, or connect a valid existing studio:
vstd cloud workspace clone WORKSPACE_ID --target my-studio
vstd cloud workspace connect WORKSPACE_ID --root my-studio
The connection is recorded as non-secret local state in
.vstd/cloud-workspace.json. It does not change the studio.yaml, deck.yaml,
or paired slide-file contract and is excluded from synchronized content and
build output.
From a connected studio, inspect and synchronize revisions with:
vstd cloud workspace status
vstd cloud workspace pull
vstd cloud workspace sync --message "Update product story"
status reports the recorded base revision, Cloud head, and whether the local
files are synchronized, unsynced, behind Cloud, or offline. Both pull and
sync checkpoint local work, reconcile against the latest Cloud version, and
update the local files directly. No GitHub repository or Git credentials are
needed for presentation storage. Cloud uses Postgres for durable writer journals
and revision heads, and private object storage for immutable source checkpoints.
Complete revision downloads have a five-minute deadline so large presentations can refresh over slower connections. Other native API requests retain their two-minute deadline. An interrupted or timed-out transfer reports an offline read error, preserves local work, and does not replay a mutation automatically. Automatic Cloud synchronization during authoring worktree begin/finish shares the five-minute allowance for the whole sync operation.
Publish the current synchronized revision, or select a revision explicitly:
vstd cloud publish create
vstd cloud publish create --revision REVISION_ID
vstd cloud publish status PUBLICATION_ID
Browser writes are acknowledged after durable Cloud persistence, independently
of background integration. A device outbox retries interrupted requests with
the same operation ID. Compatible HTML properties, Markdown sections, and deck
ordering changes merge automatically. Overlapping agent changes yield to direct
human edits; original submitted checkpoints are retained rather than discarded.
Device-draft recovery shares the original operation ID with the outbox, including
across tabs and reloads. Slide reordering refreshes the open Grid immediately,
preserving its scroll and zoom and an open companion draft. A move that finishes
during a background refresh queues another refresh instead of leaving stale
thumbnails. The synchronized file contract includes the scaffold's root .gitignore;
credentials, .git/, and .vstd/ remain excluded.
Isolated editor-session workers can read the selected deck's cached raster at
GET /api/app/decks/NAME/thumbnail.png; other catalog routes remain excluded.
The host must authorize access and run rendering in its disposable content sandbox.
Install Chromium and Poppler (pdftoppm) in raster workers. The engine gives its
headless browser a temporary loopback-only, keyed print surface; the externally
reachable editor remains bearer-protected throughout rendering.
For trusted supervisors, editor-transform accepts root instead of files to
read a validated, materialized snapshot directory without base64 transport. The
input directory is never modified. Optional delta: true returns only changed
files plus deleted paths, with delta: true identifying the response format.
An unchanged read returns empty arrays. Original full-snapshot callers remain
compatible. The supervisor must never accept a caller-supplied host path.
Cloud visual editing can use vstd editor-transform: a bounded JSON input with
deck, canonical files (path/base64 content), method, path, headers, and
base64 body produces the HTTP-shaped result and resulting canonical files.
It renders the same embedded HUD and applies the same structured slide and
companion operations as local Studio. It never starts a listener, executes slide
scripts, resolves credentials, invokes export tools, or calls providers.
Unsupported execution routes fail closed; exports and agent work stay isolated.
The caller owns authorization, durable operation IDs, acknowledgement, and
reconciliation. Temporary files are scoped to one transform and removed afterward.
Run vstd worktree begin --root STUDIO before agent editing, work in the returned
directory, then run vstd worktree finish --root WORKTREE. These commands sync
connected presentations before/after reconciliation. Add --local-only to both
commands for fully offline work. Worktrees and checkpoints remain under .vstd/.
vstd serve synchronizes connected local workspaces every two seconds while
running; otherwise run sync before opening files to fetch Cloud changes.
Pull keeps a durable, private local recovery journal before replacing files. If
interrupted, the next sync restores the pre-pull projection before reconciliation.
vstd cloud workspace recover --root DIR is also available for offline recovery.
Do not delete the recovery journal manually. Unrelated Git and local state are
preserved. Directory durability depends on the host filesystem (Windows lacks
directory fsync); recovery is not a substitute for backups.
Cloud sessions are isolated by normalized endpoint. Keep VSTD_CLOUD_ENDPOINT
consistent with the workspace association; a mismatch fails before content work.
Snapshots reject credential commands and custom credential-bearing provider
endpoints in studio.yaml; configure those providers locally via environment
variables instead. These restrictions affect cloud exchange only, not local use.
Publishing without --revision is refused when local changes or a conflict are
pending. The command reports the publication ID, selected revision, service
status, and URL. Run vstd cloud diagnostics for sanitized endpoint and
protocol compatibility details, and vstd cloud logout to revoke the session
when reachable and remove the local credential.
Open http://localhost:4400/d/product-story/.
The starter deck contains one cover slide. Edit its paired files under
decks/product-story/slides/, or ask Codex to build out the deck. The server
rebuilds on file changes and refreshes connected browsers.
In a second terminal:
cd my-studio
vstd list
vstd build product-story
The static build is written to decks/product-story/build/index.html. Build
output is disposable; edit the source files under slides/, never build/.
From the player HUD you can:
- navigate or present the deck;
- enter slide-edit mode and edit supported elements;
- open and edit the current slide's Markdown companion;
- attach or inspect source files;
- paste an image from the clipboard onto the slide;
- leave a sticky-note instruction for the redesign agent;
- download PDF or editable PowerPoint.
Use Vessica Studio with Codex
Vessica Studio keeps its authoring instructions in seven canonical skills:
| Skill | Use it for |
|---|---|
cloud-presentation |
Open or create a Cloud presentation from any Codex chat |
deck-new |
Frame, outline, create, and visually review a new deck |
slide-add |
Insert one or more context-aware slides into an existing narrative |
slide-edit |
Edit content or layout while preserving the companion contract |
deck-fork |
Tailor a deck for a client, audience, industry, or event |
deck-review |
Run whole-deck visual, narrative, and companion QA |
market-refresh |
Re-research dated claims and update time-sensitive slides |
The skill files are embedded in vstd; the CLI can list or print them at any
time:
vstd skill
vstd skill conventions
vstd skill slide-edit
Install the Codex plugin
Add the repository's marketplace, then install Vessica Studio:
codex plugin marketplace add vessica-labs/vessica-studio --ref main
codex plugin add vessica-studio@vessica-studio
Start a new Codex task after installation so the seven plugin skills are
discovered. In Codex CLI, /plugins opens the plugin browser; in Codex desktop,
use the Plugins surface. Verify the install with codex plugin list.
The repository also retains legacy thin prompt launchers for environments that
do not support plugins. Clone the repository and run ./codex/install.sh; it
copies launchers into ${CODEX_HOME:-$HOME/.codex}/prompts. The launchers call
vstd skill at runtime and require a new Codex session after installation.
Use a launcher explicitly:
/vstd-cloud-presentation Open the Acme strategy presentation from Cloud and edit slide 40
/vstd-deck-new Build a 12-slide board presentation about our product strategy
/vstd-slide-add Add a customer proof slide after 0040
/vstd-slide-edit Redesign slide 0060 around the attached source PDF
/vstd-deck-review Review the entire deck before tomorrow's presentation
/vstd-deck-fork Fork product-story for Acme's executive team
/vstd-market-refresh Refresh every market statistic through August 2026
You can also ask Codex naturally. A useful request names the studio root or deck, the audience, the desired outcome, and any constraints:
Use the Vessica Studio slide-edit workflow to simplify slide 0070 in the
product-story deck. Preserve its evidence and talk-track transition, keep the
chart editable, and run a visual critic pass.
Codex should read vstd skill conventions before deck work and the relevant
workflow before acting. Copy codex/AGENTS.md into a studio
content repository to make those rules persistent for that repository. The
guide tells Codex to preserve slide/companion pairs, use the canonical skills,
and avoid generated build output.
Plugin skills may be invoked explicitly from Codex's skill picker or with the
$ skill syntax, for example $vessica-studio:slide-edit. Codex can also select
a skill implicitly from its description. See the
Codex plugin documentation and
Codex skills documentation for
current installation, discovery, and invocation behavior.
What the skills enforce
The workflows are more than prompt templates. They preserve the authoring contract:
- Read the companion before changing a slide.
- Keep factual claims aligned with evidence and sources.
- Update the talk track and its transition to the next slide.
- Record the completed change in the companion log.
- Give every generated slide a meaningful visual.
- Run a visual critic pass after layout or design changes.
- Compare source-backed slides directly with the attached original.
The source of truth lives in plugin/skills and
plugin/docs/conventions.md. The Codex launchers
do not duplicate those instructions.
Claude Code and Cowork
The same workflows are packaged as a Claude plugin:
/plugin marketplace add vessica-labs/vessica-studio
/plugin install vessica-studio@vessica-studio
Start a new session, ask naturally for a deck task, or invoke a workflow such
as /vessica-studio:deck-new. Both integrations ultimately read the same files
embedded in vstd.
How a studio is organized
my-studio/
├── studio.yaml # engine, model, port, and storage settings
├── themes/
│ └── default/
│ ├── theme.css # presentation styling
│ └── tokens.json # design tokens
├── library/
│ ├── manifest.json # shared image and video catalog
│ ├── img/ # generated or uploaded images
│ ├── video/ # local video bytes; keep out of Git
│ └── video-posters/ # generated poster frames
├── requests/ # asynchronous media and redesign requests
├── site/ # optional public homepage and assets
└── decks/
└── product-story/
├── deck.yaml # title, theme, visibility, fork provenance
├── deck.css # deck-specific style overrides
├── sources/ # original PDFs, PPTX files, images, and documents
├── build/
│ └── assets/powerpoint/ # disposable per-slide PowerPoint cache
└── slides/
├── 0010-cover.html # one root <section class="slide">
├── 0010-cover.md # companion evidence, intent, talk track, and log
└── 0010-cover.link.yaml # optional Vessica-managed slide link provenance
The slide pair
Each slide has two source files with the same basename:
NNNN-slug.htmlcontains exactly one root<section class="slide">. Slide order follows the numeric filename prefix; sparse numbering such as0010,0020, and0030leaves room to insert.NNNN-slug.mdcontains frontmatter plusIntent,Key ideas,Evidence & sources,Talk track,Visual direction, andLogsections.
The companion is part of the content model, not optional documentation. It is the agent's context, evidence record, and presenter cue sheet.
A linked slide still has the normal HTML/Markdown pair as a durable snapshot.
Its optional .link.yaml sidecar points to the source deck and slide and records
source hashes and the last refresh. Linked target slides are read-only until
detached. When the target owner retains source access, Studio refreshes changed
HTML, companion content, and source attachments. If the source disappears or
access is revoked, the last snapshot continues to render and is marked stale to
presenters; audience views and exports show only the slide. Copying a linked
slide flattens its latest snapshot into an ordinary editable slide.
Source files used to create a slide live in decks/<deck>/sources/ and are
declared in companion frontmatter:
attachments:
- name: source-deck.pptx
path: sources/0030-source-deck-a1b2c3.pptx
media_type: application/vnd.openxmlformats-officedocument.presentationml.presentation
page: 3
Engine-owned player, theme-owned presentation styling
The player and HUD are embedded in the engine. Themes provide theme.css and
design tokens; they do not replace the player markup. This keeps navigation,
editing, export, Vessica, video controls, and audience behavior consistent
across themes.
Slides use a fixed 1280×720 canvas. Mark an element with
data-current-month-year to render the viewer's current month and year, such as
August 2026, at runtime and in browser-backed exports.
CLI manual
Run vstd help for the installed command summary. Examples below assume the
current directory contains studio.yaml. Most studio commands also accept
--root DIR; it is a per-command flag, not a global flag.
Create and inspect studios
vstd init [dir]
Scaffold a studio root without overwriting existing files:
vstd init quarterly-presentations
Creates studio.yaml, the default theme, an empty asset manifest, decks/,
library/, and requests/.
vstd new <deck> [--title T] [--root DIR]
Create a deck with metadata, deck CSS, and a paired starter cover slide:
vstd new operating-model --title "The AI Operating Model"
Deck names use lowercase letters, digits, and hyphens.
vstd list [--root DIR]
List every deck, slide count, title, and fork parent:
vstd list
Build, serve, and present
vstd build <deck> [--root=DIR]
vstd build --all [--root=DIR]
Assemble one deck or every deck into generated build/index.html files:
vstd build operating-model
vstd build --all
vstd release-build [deck] --output DIR [--root DIR]
Build one deck for immutable hosted delivery through the public engine contract.
The command requires a revision-identifiable, unmodified vstd binary and an
empty output directory. It writes a self-contained index.html, only the
referenced release assets, and release-manifest.json with the exact engine
version/revision plus deterministic artifact and manifest SHA-256 checksums.
When deck is omitted, the studio must contain exactly one deck.
vstd release-build product-story --output /tmp/product-story-release
Optional delivery flags preserve editable originals and produce host-ready artifacts:
vstd release-build product-story --output /tmp/product-story-release --delivery-template --optimize-delivery
vstd delivery-resources --output /tmp/vstd-platform-assets
--delivery-template emits typed vstd-asset:<sha256>:<base64url-path> slots.
An authorized host must replace only manifest members with its delivery URLs;
vstd-session:keepalive accepts a cookie-authenticated renewal URL or an empty string.
Image and CSS background references, including quoted URLs in inline styles, use
the same manifest-scoped delivery paths. Templates are not directly browsable until these slots are resolved. Ordinary
release-build remains self-contained and needs no Cloud account or network.
--optimize-delivery requires cwebp for raster images and FFmpeg for videos.
It emits transparent WebP variants up to 640/1280/1920 pixels without upscaling,
responsive srcsets, sanitized SVG, minified HTML/CSS/JS, H.264 MP4 capped at 1080p
with fast-start, and WebP posters. Variants and dimensions are optional artifact
fields; delivery settings and variant rules participate in the deterministic
manifest checksum. Original studio files are retained unchanged. Encoder versions
should be pinned consistently across build workers.
delivery-resources exports only compiled-in engine/default-theme CSS named by
its digest. Hosts install this trusted output independently of tenant uploads;
custom themes and deck overrides remain presentation scoped. A host must not
trust a tenant-provided path as permission to populate its shared namespace.
The data-only editor-transform protocol also accepts optional asset_urls
(logical /library/... or /assets/video/... paths mapped to HTTPS URLs) and
asset_session_url. The engine applies these at render time, uses direct video
URLs, and restores logical paths on saves. Hosts provide access metadata; the
engine continues to own rendering, progressive loading and editor serialization.
The release output contains no companion Markdown or authoring source. Missing, unsafe, symlink-escaped, oversized, or mutable assets fail the build rather than producing a partial release.
Consumers installing directly from an immutable Git revision must embed the
same full revision so the binary can verify and report it. For example, with
REVISION set to a 40-character commit SHA:
go install -ldflags "-X main.releaseBuildRevision=$REVISION" \
"github.com/vessica-labs/vessica-studio/cmd/vstd@$REVISION"
Running vstd build without a deck also builds all decks.
When building from outside the studio directory, use the --root=DIR form so
the deck name remains unambiguous to the current command parser.
vstd serve [deck] [flags]
Build, watch, live reload, and expose the player and HTTP editing surface:
vstd serve operating-model
vstd serve operating-model --port 4500
vstd serve --mode present
vstd serve --mode public
vstd serve operating-model --agent
Flags:
| Flag | Meaning |
|---|---|
--root DIR |
Studio root; default . |
--port N |
Override studio.yaml, PORT, and the default port |
--mode studio|present|public |
Select the authorization and editing mode |
--agent |
Run the optional redesign worker alongside the server |
Serving modes:
| Mode | Intended use | Content writes | Access model |
|---|---|---|---|
studio |
Local authoring | Yes | Local operator is the presenter |
present |
Local presenting | No | Local operator is the presenter |
public |
Hosted presenting | Presenter-only when content sync is enabled | GitHub-allowlisted presenter or signed audience link |
Do not expose studio mode to an untrusted network. Its write routes trust the
local operator.
PDF and PowerPoint export
Start vstd serve, open a deck, choose Entire deck or Current slide in
the Download menu, and then choose PDF, visual-exact PowerPoint, or editable
PowerPoint. Presenters receive all three formats; audience sessions receive PDF
only. Browser-backed export requires Chrome or Chromium on the machine running
vstd; visual-exact PowerPoint rasterization also requires Poppler.
Editable capture uses a disposable loopback DevTools connection and waits for the
object model to finish, including asynchronous image and CSS background decoding.
The HTTP routes accept an optional non-parked slide ID, for example
/api/deck/operating-model/export.pdf?slide=0030-economics and
/api/deck/operating-model/export.pptx?mode=editable&slide=0030-economics.
Without slide, they preserve the existing whole-deck behavior.
PDF is a fixed-layout document and is regenerated per request. PowerPoint
stores disposable per-slide artifacts and a manifest under
decks/<deck>/build/assets/powerpoint/. Repeating an export reuses unchanged
slides; changing a fragment, referenced local asset, theme CSS, or deck CSS
invalidates only the affected entries (theme/deck CSS naturally affect the
whole deck). The cache is excluded from Git, content sync, file-watcher reloads,
bundles, and source-asset manifests. Delete that directory at any time to force
a clean rebuild. Editable PowerPoint converts supported rendered elements into
native PresentationML objects. Text retains browser wrapping, padding and inline
styles; SVG paths retain editable subpaths, Bézier controls, gradient stops and
dash patterns. Generated CSS bullets and arrows are included. Complex CSS
backgrounds, masks and shadows become individual pictures, while text and
ordinary shapes remain editable. The installed font that Chromium actually
uses is recorded; the same font should be available on the PowerPoint machine.
Registered MP4 assets are embedded with their posters in editable exports.
In PowerPoint slide show, the first advance on a video slide starts playback
using the native click sequence; the media remains a selectable video object.
Source bytes must exist under library/video/ or in the configured storage
bucket and match the manifest digest and size. Missing or corrupt media fails
export explicitly. The unique video budget is 512 MiB; repeated media is
packaged once. Hosts with isolated execution can first request the same editable
export URL with media=plan to get { "videos": ["registered-id"] } for the
browser-measured selection. Materialize those manifest-verified MP4 files into
the same content root, then request the ordinary editable export. The plan uses
the same presenter authorization and cached DOM capture. Simulator bundles
retain their static backdrop only. Complex
browser effects may still differ, so review the downloaded deck before delivery.
vstd bundle <deck> [--root DIR]
Create a portable folder containing index.html and the referenced local media:
vstd bundle operating-model
Output is written to decks/<deck>/build/bundle/. Keep the folder together when
presenting offline. This is the preferred static export for video-bearing decks.
Fork and maintain deck variants
vstd fork <deck> <client> [--root DIR]
Create decks/<deck>--<client> with fork provenance and per-slide parent
hashes:
vstd fork operating-model acme
vstd diff-upstream <fork> [--root DIR]
Report slides added, removed, or changed in the parent since the fork:
vstd diff-upstream operating-model--acme
This is a detection tool, not an automatic merge. Use the deck-fork or
slide-edit workflow to port upstream improvements while preserving client
customizations.
Images and the shared asset library
vstd asset gen
Generate and register an image:
vstd asset gen \
--prompt "Editorial illustration of human and AI collaboration" \
--family editorial \
--tags workforce,collaboration \
--size 1536x1024 \
--slug human-ai-team
Flags:
| Flag | Meaning |
|---|---|
--prompt P |
Required image prompt |
--family F |
Style family from library/manifest.json |
--tags a,b |
Comma-separated retrieval tags |
--size WxH |
Requested image size; default 1024x1024 |
--slug S |
Stable asset ID slug |
--dry-run |
Show the resolved prompt and ID without calling the API |
--root DIR |
Studio root |
Generated files are cataloged in library/manifest.json and referenced from
slides as /library/<file>. Reuse an existing asset before generating a near
duplicate.
Image registration preserves existing image source attribution and unknown asset metadata in library/manifest.json. Adding a generated image does not discard metadata on existing references.
vstd asset list and vstd asset find
Browse the manifest or search by family and tags:
vstd asset list
vstd asset find --tags workforce,collaboration
vstd asset find --family editorial
Tag matching is “any of” the supplied tags. find prints a generation hint when
no asset matches.
Interactive application bundles
Use a self-contained ZIP for a simulator or other interactive slide application:
vstd asset add-bundle ./simulation.zip --slug simulation --entrypoint index.html --poster img/simulation.jpg
vstd asset inspect-bundle ./simulation.zip --entrypoint index.html --json
The library manifest stores a bundles entry with id, content-addressed
file (bundle/<sha256>.zip), hash, bytes, entrypoint, expandedBytes,
and fileCount, plus optional poster and created. Keep library/bundle/
out of Git. ZIPs stay outside the canonical Cloud snapshot; native sync uploads
verified bytes separately, using the additive asset.bundle.write capability,
before syncing their manifest. Import the original ZIP for local offline use;
Cloud-only checkouts may retain references without the local archive.
A slide launches a bundle explicitly:
<div data-vstd-bundle="simulation" style="position:relative;width:100%;height:100%" aria-label="Simulation">
<button data-bundle-launch>Start simulation</button>
<span data-bundle-status role="status"></span>
</div>
Version 1 supports inline application JavaScript and CSS with relative data and
image files. Bundle module dependencies into inline code before importing;
external scripts, import maps, embedded frames, network services, workers and
parent-document access are unsupported. The app runs in an opaque-origin
allow-scripts sandbox. Archives download only after launch, including in Cloud,
and leaving the slide destroys the frame. Thumbnails and exports show the
slide's authored static preview. Limits are 128 MiB compressed, 256 MiB expanded,
512 files and an 8 MiB HTML entrypoint. Paths, entry types, collisions, CRCs,
sizes and manifest integrity are validated without executing the app.
Immutable releases include the ZIP and an engine-generated download shell
marked bundle-relay-v1; the authored presentation remains sandboxed in
presentation.html. The shell preserves the viewer query and slide fragment
when opening that document, so unlisted links remain authorized.
Hosts may give only that verified entrypoint authority to
fetch declared release assets and transfer bytes into its opaque child. Never
give application frames cookies, delivery URLs or a credentialed null-origin
CORS allowance. Serve ZIPs as binary attachments, never application HTML from a
credentialed Studio origin.
Video assets and object storage
vstd asset add-video <file>
Inspect, optionally normalize, posterize, hash, and register a video:
vstd asset add-video ./demo.mov --slug product-demo --tags demo,product
Flags:
| Flag | Meaning |
|---|---|
--slug S |
Asset ID; defaults from the filename |
--tags a,b |
Comma-separated retrieval tags |
--no-transcode |
Keep the original bytes while still hashing and posterizing |
--root DIR |
Studio root |
When storage is configured, ingestion also uploads missing bytes. Reference a cataloged video by ID, never by a hard-coded source path:
<video class="vid" data-vstd-video="product-demo" data-autoplay data-loop></video>
The player owns source resolution and playback. Video slides include a persistent sound on/off control for editing and presenting.
vstd asset push and vstd asset pull
Synchronize manifest-referenced video files with an S3-compatible bucket:
vstd asset push
vstd asset pull
push uploads missing objects. pull restores video bytes missing from a fresh
checkout. Keep library/video/ out of Git; commit the manifest and posters.
Editable chart migration
Charts should use a hybrid structure by default: inline SVG for plotted geometry and positioned HTML for human-readable text. The chart can be selected as a group while labels remain editable and export as PowerPoint text boxes.
vstd chart promote-text <deck> <slide>
Preview and migrate text from an existing inline SVG chart:
vstd chart promote-text operating-model 0060-growth --dry-run
vstd chart promote-text operating-model 0060-growth
Additional flags are --root DIR and --chromium PATH. The command uses
rendered browser geometry, appends a companion log entry, and refuses partial
writes when it encounters unsupported curved or zero-size text. It does not
migrate raster charts.
Agent and skill commands
vstd skill [name]
List embedded skills or print one workflow/reference document:
vstd skill
vstd skill conventions
vstd skill deck-review
The output is plain Markdown designed to be read by any coding agent.
vstd agent [--root DIR]
Run one synchronous sweep of queued redesign requests:
VSTD_AGENT=1 vstd agent
Use vstd serve --agent or VSTD_AGENT=1 vstd serve for a continuous background
worker. The default coding agent is Claude pinned to claude-opus-5-5; set VSTD_AGENT_CMD or the related
agent environment variables to use another command and tune limits. Hosted
Codex workers must set VSTD_AGENT_SANDBOX=railway: the API service dispatches
each pass to a disposable, network-isolated Railway Sandbox and refuses to run
Codex in-process in public mode. The dispatcher requires a project-scoped
RAILWAY_TOKEN, RAILWAY_ENVIRONMENT_ID, and the Railway JavaScript SDK at
VSTD_RAILWAY_SDK (default /opt/vstd-sandbox/node_modules/railway/dist/index.js).
The sandbox receives only the requested deck, its theme and referenced library
assets, matching asset requests, the vstd binary, and an OpenAI credential
resolved by Railway variable reference.
Keys and audience links
vstd key check [--root DIR]
Confirm that an OpenAI key resolves without printing the key or its source:
vstd key check
vstd qr <deck>
Mint an expiring, signed, deck-scoped audience URL and save a QR image:
vstd qr operating-model --ttl 24
vstd qr operating-model --ttl 72 --host https://decks.example.com
The signing secret comes from VSTD_SECRET or share_secret_cmd. The QR image
is saved to decks/<deck>/build/share-qr.png.
Railway commands
vstd railway up
Configure and deploy a content repository that already contains its deployment files:
vstd railway up --dry-run
vstd railway up --client-id GITHUB_OAUTH_CLIENT_ID --allowed octocat
Flags:
| Flag | Meaning |
|---|---|
--root DIR |
Studio root |
--client-id ID |
GitHub OAuth app client ID with Device Flow enabled |
--allowed a,b |
Comma-separated presenter GitHub logins |
--github-token TOKEN |
Read token for cloning a private engine repository |
--with-openai-key |
Copy the locally resolved OpenAI key without prompting |
--dry-run |
Print the deployment plan without calling Railway |
The command ensures the Railway CLI is available, signs in if needed, links or
creates a project, sets core variables, deploys, obtains a domain, and stores the
public host in studio.yaml. It requires a Dockerfile and the related
deployment files in the content repository.
vstd railway status and passthrough
vstd railway status
vstd railway logs
vstd railway variables
status reports the linked service and saved public host. Any other arguments
are passed to the Railway CLI from the studio root.
Help and version
vstd help
vstd version
help prints the installed command and environment summary. version is useful
when reporting a bug or confirming which engine build a hosted studio runs.
Studio and player features
Direct editing, the ribbon, and the slide panel
Press E (or choose Edit in the player HUD) to enter edit mode. The bottom HUD gives way to a ribbon across the top of the window: home, deck title, slide stepper, new slide, grid, agenda, notes, sticky, companion, knowledge base, Vessica, a save indicator, download, share, Present, and Done. Every ribbon control shows its name and keyboard shortcut in a tooltip on hover or focus. A slide panel on the left lists thumbnails of every slide: click one to jump to it, drag one to reorder, and use New slide at the bottom to insert after the current slide. The panel collapses from its header button and remembers that choice.
Select a supported text, shape, image, or chart element on the slide and an object bar slides in under the ribbon with the controls that apply to that object: edit text, bold, italic, underline, text size, text/fill/line color, picture crop and zoom, video playback options, reset, and delete. It disappears again when nothing is selected, so the canvas stays clear.
Edit-mode shortcuts: Enter edits the selected text, ⌘B / ⌘I / ⌘U toggle bold, italic, and underline, ⌘⇧> and ⌘⇧< change text size, ⌘D duplicates the selection, C toggles crop on a picture, Delete removes the selection, arrow keys nudge (Shift for larger steps), ⌥ while dragging disables snapping, ⌘Z / ⌘⇧Z undo and redo, ⌘S saves now, and Esc deselects, then leaves edit mode. Keyboard shortcuts are suppressed while typing in sticky notes, companion fields, dialogs, or editable elements. Edits are also saved automatically when you leave edit mode; the ribbon indicator shows Saved, Unsaved, Saving…, or Conflict when a slide changed on disk after the page loaded.
Pictures include both <img> elements and elements whose CSS uses a background
image. Select a picture and choose Crop to drag the image within its fixed
frame; the arrow keys provide fine positioning. CSS background pictures also
expose Zoom − and Zoom + controls to loosen or tighten the crop. These
position and size changes are saved with the slide fragment like other direct
edits.
Drag across blank slide canvas to draw a marquee around multiple objects. The selected objects receive one combined outline; drag any selected object to move the group, use the arrow keys to nudge it, or press Delete to remove every selected object in one undoable edit. Object-specific formatting, resizing, and picture cropping remain available when exactly one object is selected.
Companion drawer and source attachments
The companion button opens the current slide's Markdown in a side drawer. The drawer renders the document for reading and supports direct editing. Vessica can open the companion and apply dictated narrative edits.
Attach a PDF, PPT/PPTX, document, spreadsheet, or image to the companion. The file becomes part of the slide's source record and is available to the editing agent. A source-aware request can be as direct as:
Look at exhibit A on page 12 of the attached PDF and add the missing customer
segment detail to this slide.
Images from the clipboard
Enter slide-edit mode, copy an image, and paste it onto the slide. Vessica Studio adds the image to the library, content-hash deduplicates it, and creates an editable canvas element.
Hybrid editable charts
Use one data-chart-group container containing:
- an SVG
.chart-artlayer for axes, gridlines, lines, bars, areas, dots, and decorative geometry; and - absolutely positioned
.chart-label[data-edit]HTML elements for ticks, legends, values, annotations, and other text.
Keep an accessible description on the chart geometry with aria-label,
<title>, or <desc>. Vessica can highlight a chart when the spoken topic
matches its accessible description or visible labels. Slide titles are excluded
from Vessica highlighting.
Interactive simulation controls
Vessica can launch/resume, pause, jump to a checkpoint, change playback speed,
select a camera, or request the actual state of an opted-in simulation on the
active slide. For example: “Vessica, pause the simulation” or “Vessica, switch to
the cockpit.” control_simulation accepts action (start, stop, checkpoint,
speed, camera, or status), plus the matching checkpoint, speed, or
camera argument. Stop pauses and retains position. Only explicit simulator
requests trigger these controls; stopping the flight does not end the conversation.
An iframe opts in with its supported values and a same-origin launch URL:
<iframe data-vstd-simulation='{"name":"Flight","checkpoints":["Dzong"],"speeds":[1,2,4],"cameras":["director","cockpit"]}'
data-vstd-simulation-src="/site/flight/" srcdoc="Launch screen"></iframe>
An optional comma-separated data-vstd-simulation-hosts restricts availability
to named hosts, useful while a runtime is local only. Thumbnails, hidden slides,
and audience clients cannot issue commands. Launching requires start; subsequent
commands return the simulator's confirmed state, or an error after a bounded wait.
For cataloged applications, put data-vstd-simulation on the
data-vstd-bundle container instead of an iframe. The tool uses the existing
bundle loader, verifies the archive, and controls only that container's opaque
application frame. The bootstrap supplies window.VSTDSimulationHost.origin
for validating parent messages without accessing parent credentials or DOM.
An opaque child replies with origin null; the player checks its exact window
identity as well as that origin. Leaving the slide destroys this sandbox and
returning starts a fresh session, as with manually launched bundles.
The embedded application listens for parent postMessage requests with type
vstd:simulation:command, a unique id, action, and its matching argument. It
must validate the parent source/origin and permitted values, and respect lifecycle;
deduplicate by id; and pause rendering/playback when its slide is inactive.
Reply to the exact parent origin with vstd:simulation:accepted while loading,
then vstd:simulation:result with the same id and either {ok:true,state:{...}}
or {ok:false,error:"..."}. The player verifies frame source and origin, cancels
requests on slide/auth changes, and includes the capability catalog in Vessica's
current-slide instructions. No arbitrary code or iframe URL is supplied by the
voice tool. This contract controls an available runtime; it does not upload or
host application assets.
Video playback
The player starts and stops cataloged videos as slides enter and leave. Video
slides expose a small persistent sound switch so editing does not repeatedly
trigger audio. Use data attributes such as data-autoplay, data-loop,
data-unmuted, and data-keep-time to control behavior.
Configuration
A new studio.yaml starts with:
theme_default: default
port: 4400
openai:
image_model: gpt-image-2
realtime_model: gpt-realtime-2
base_url: https://api.openai.com/v1
Other supported top-level settings include app_host, public_host,
share_secret_cmd, and storage. In hosted collaboration mode, app_host is
the authenticated catalog/team origin and public_host is the separate player
and audience origin.
OpenAI
Key resolution order is:
OPENAI_API_KEYVSTD_OPENAI_KEYopenai.api_key_cmdinstudio.yaml
Example using macOS Keychain:
security add-generic-password -U -s vessica-openai -a "$USER" -w 'sk-...'
openai:
image_model: gpt-image-2
realtime_model: gpt-realtime-2
base_url: https://api.openai.com/v1
api_key_cmd: security find-generic-password -s vessica-openai -w
Run vstd key check after configuring the resolver.
S3-compatible storage
Storage can be configured in YAML:
storage:
endpoint: https://s3.example.com
bucket: presentation-media
region: us-east-1
access_key_cmd: security find-generic-password -s vstd-s3-access -w
secret_key_cmd: security find-generic-password -s vstd-s3-secret -w
Or with environment variables:
| Variable | Purpose |
|---|---|
VSTD_S3_ENDPOINT |
S3-compatible API endpoint |
VSTD_S3_BUCKET |
Bucket name |
VSTD_S3_REGION |
Region |
VSTD_S3_ACCESS_KEY |
Access key |
VSTD_S3_SECRET_KEY |
Secret key |
Environment variables take precedence over YAML.
Runtime and hosted environment variables
| Variable | Purpose |
|---|---|
PORT |
Override the configured HTTP port; Railway sets this |
VSTD_MODE |
studio, present, or public |
VSTD_CHROME / VSTD_CHROMIUM |
Browser executable for exports and migration |
VSTD_SECRET |
Sign presenter sessions and audience links |
VSTD_GITHUB_CLIENT_ID |
GitHub OAuth application with Device Flow enabled |
VSTD_ALLOWED_GITHUB |
Comma-separated presenter login allowlist |
VSTD_COLLABORATION |
Set to 1 to enable PostgreSQL-backed single-team collaboration |
DATABASE_URL |
PostgreSQL connection URL; required in collaboration mode |
VSTD_APP_ORIGIN |
Exact HTTPS origin for marketing, authentication, catalogs, and team administration |
VSTD_PLAYER_ORIGIN |
Separate exact HTTPS origin for deck execution, audience links, and player APIs |
VSTD_DOCS_URL |
Documentation-site URL opened from the presentation catalog; defaults to https://studio-docs.vessica.ai |
VSTD_OWNER_GITHUB_LOGIN |
GitHub login that may bootstrap and administer the team |
PUBLIC_URL |
Public base URL for audience and action links |
VSTD_CONTENT_SYNC |
Set to 1 to enable authenticated hosted editing |
VSTD_GIT_REPO |
Git content repository URL |
VSTD_GIT_BRANCH |
Content branch to push and poll |
VSTD_GIT_TOKEN |
Repository-scoped content write token |
VSTD_GIT_DEBOUNCE_SECONDS |
Delay before batching hosted edits into a push |
VSTD_GIT_POLL_SECONDS |
Remote polling interval |
VSTD_AGENT |
Set to 1 to enable the redesign worker |
VSTD_AGENT_CMD |
Agent command or supported agent name |
VSTD_AGENT_SANDBOX |
Set to railway for hosted Codex execution; required for Codex in public mode |
VSTD_AGENT_SANDBOX_SECRET_SERVICE |
Railway service name that owns the Codex credential; defaults to RAILWAY_SERVICE_NAME |
VSTD_AGENT_SANDBOX_SECRET_VARIABLE |
Railway variable referenced as the sandbox Codex credential; defaults to OPENAI_API_KEY |
VSTD_RAILWAY_SDK |
Absolute path to the Railway JavaScript SDK entrypoint |
RAILWAY_TOKEN |
Project-scoped Railway token used only by the sandbox dispatcher |
VSTD_AGENT_TIMEOUT |
Redesign timeout, such as 30m |
VSTD_AGENT_CRITIC_TIMEOUT |
Source-critic timeout, such as 20m |
VSTD_AGENT_CONCURRENCY |
Maximum concurrent redesign jobs |
VSTD_AGENT_MAX_PER_HOUR |
Hourly redesign-job limit |
VSTD_GIT_PUSH |
Set to 1 to commit and push completed redesign passes |
VSTD_GIT_REMOTE |
Bootstrap remote used by the legacy redesign-worker Git path |
VSTD_TOOLS_MODEL |
Model used for Vessica tools |
VSTD_TRANSCRIBE_MODEL |
Model used for transcription |
TELNYX_API_KEY, TELNYX_FROM_NUMBER, TELNYX_CONNECTION_ID |
Optional SMS and calling actions |
RESEND_API_KEY, RESEND_FROM, VSTD_CONTACT_TO |
Optional email and public contact actions |
Never place credentials in slide files, deck metadata, built output, manifests, fixtures, or Git history.
Hosted deployment
public mode keeps audiences read-only. Presenters authenticate through GitHub
Device Flow and must appear in VSTD_ALLOWED_GITHUB. Successful sign-in opens
the non-cacheable /presentations index so a public marketing homepage at /
cannot mask the authenticated presenter session.
When VSTD_CONTENT_SYNC=1, an authenticated presenter can use sticky-note,
direct-edit, companion, attachment, and pasted-image controls in the hosted
player. A save updates the running instance immediately. A background worker
then batches content-only commits to the configured Git repository and polls the
branch for remote changes.
The Git token is sent through an ephemeral authorization header; it is not
written into the remote URL, command arguments, or .git/config. Scope it to
Contents read/write on the content repository only.
To keep content saves from redeploying the application, configure Railway watch paths around deployment and engine files rather than the deck content paths. Video bytes remain outside Git and are synchronized through object storage.
Without content sync, public mode remains read-only. Audiences enter through
expiring deck-scoped links created by vstd qr.
Team collaboration mode
Collaboration mode is additive and disabled by default. It requires public mode, PostgreSQL, and separate app/player origins:
app_host: https://studio.example.com
public_host: https://present.example.com
VSTD_MODE=public
VSTD_COLLABORATION=1
DATABASE_URL=postgresql://...
VSTD_APP_ORIGIN=https://studio.example.com
VSTD_PLAYER_ORIGIN=https://present.example.com
VSTD_OWNER_GITHUB_LOGIN=octocat
Startup runs additive schema migrations under a PostgreSQL advisory lock and
reconciles filesystem decks into the catalog. The first matching GitHub login
becomes the owner and claims previously unowned decks as private. The owner can
invite members from /team; invitation and password-reset email uses
RESEND_API_KEY and RESEND_FROM.
The owner signs in through a guided GitHub device flow. Vessica opens GitHub in
a new tab, keeps the temporary code visible with its remaining lifetime, and
copies it when the code is clicked. The in-progress flow is retained in tab
sessionStorage, so a reload or temporary network interruption does not force
the owner to race for or lose the code.
Each user owns their decks. Team visibility grants other active members View, Present, export, and Fork access, but never Edit or sharing authority. Forks are private copies with upstream hashes and provenance. Removing a member revokes their sessions immediately and transfers their decks to the owner without changing visibility.
The presentation directory adds one-level personal folders plus All, Mine,
Shared, and Recent views. Folder placement belongs only to the signed-in user:
moving a shared deck does not change its owner, visibility, team permissions, or
any teammate's organization. A permanent personal Trash folder keeps removed
presentations intact and out of the normal catalog views; moving them to another
folder restores them. Trash cannot be renamed, deleted, or emptied. Deleting a
regular folder returns its presentations to the root. Local serving persists the
same organization as an atomic mode-0600 JSON file in the OS user-config
directory, keyed by the canonical studio root, so it never enters deck content
or Git.
Slide transfer requires Fork access to the source and Edit access to an owned target. Copy creates a normal target-themed slide pair and content-deduplicated copies of companion attachments. Link creates a target-themed, read-only live snapshot; link-to-link chains are rejected. Player-grid selections cross to the app picker through a five-minute single-use intent bound to the authenticated user, carried only in the URL fragment. Retained linked snapshots intentionally remain in the target after source access is revoked; detach them if that continuity behavior is not appropriate for a particular deck.
The account cookie is host-only on the app origin and is never player
authorization. App launches exchange a 60-second, single-use handoff for a
12-hour deck/mode bearer kept in tab sessionStorage; every player request
revalidates membership, deck ownership, visibility, and mode. Native media uses
a separate player-host-only HttpOnly cookie accepted only by media handlers.
Keep the deployment at one application replica while its writable Git checkout
remains a single-writer process.
Owner monitoring
Collaboration mode also enables an owner-only /observability workspace,
linked from the presentation catalog immediately above Documentation. It shows
7-, 30-, and 90-day audience summaries, named QR-chat participants, anonymous
privacy-preserving visitor labels, presentation and slide views, team activity,
sanitized HTTP 5xx routes, and model token usage from both direct OpenAI API
requests and Codex agent runs dispatched to Railway Sandboxes.
Audience and player requests never wait for analytics writes. Events enter a bounded in-process queue and are flushed to PostgreSQL in small background batches; if that queue reaches capacity, events are dropped and the count is shown to the owner. Viewer records use a random first-party HttpOnly identifier. The engine does not store viewer IP addresses, user agents, share tokens, prompts, model responses, or error response bodies in observability data. OpenAI API totals come from response usage and authenticated Realtime events. Codex totals come from the privacy-safe model, session, and total-token summary printed by each CLI run. Sandbox execution metadata records the disposable sandbox identifier for attribution; the CLI does not report an input/output split, so that breakdown is intentionally left blank in the dashboard. The owner route and its JSON API re-check the permanent team-owner capability on every request; ordinary team members and the player origin cannot access them.
To roll back application behavior without discarding collaboration data,
disable VSTD_COLLABORATION, restore the former canonical public_host, and
redeploy. Migrations are additive and are not reversed.
See docs/THREAT_MODEL.md for trust boundaries,
existing controls, and hardening recommendations.
Architecture
| Package | Responsibility |
|---|---|
cmd/vstd |
CLI parsing and orchestration, including Railway operations |
internal/studio |
Configuration, deck/slide persistence, builds, forks, and export |
internal/library |
Shared image/video catalog types and manifest persistence |
internal/oai |
OpenAI authentication and API calls |
internal/video |
Video inspection, normalization, poster extraction, and registration |
internal/s3 |
S3-compatible storage and request signing |
internal/server |
HTTP routes, modes, auth, editing, exports, audience tools, sync, and workers |
plugin |
Canonical agent skills and authoring conventions |
The CLI and server adapters depend on domain packages. The neutral studio and library models do not depend on OpenAI, S3, Railway, or agent runtimes.
Development and contributing
Issues and focused pull requests are welcome. Preserve the file contract and public compatibility surfaces, keep credentials out of fixtures and logs, and add tests for behavior changes.
Build and run the standard checks:
make build
go test ./...
go test -race ./...
go vet ./...
The full contributor guide, package ownership map, and validation contract are
in AGENTS.md. Tests use local fixtures and do not require live
OpenAI, GitHub, Railway, Telnyx, Resend, or S3 credentials.
License
Apache License 2.0 © Vessica Labs
Hosted browser editor transport
Hosted services can run the existing player/HUD through
vstd editor-session in an isolated disposable content
sandbox. This additive gateway-authenticated transport provides canonical
snapshots; the host remains responsible for tenant authorization and committing
saved revisions. It does not change local vstd serve or read-only delivery.
When Cloud advertises workspace.sync.incremental, sync and pull transfer only
changed file bytes. Unchanged assets use verified SHA-256 references to the
exact base revision or captured local snapshot. Deletions and online edits still
use the normal reconciliation rules; older servers retain full-snapshot sync.
See the incremental sync protocol.
Native Cloud commands default to https://studio.vessica.ai. Set
VSTD_CLOUD_ENDPOINT to use another service (including an existing connection
created against a different endpoint). vstd cloud login prints the service's
prefilled device approval link when available; approve the displayed code in
your browser. Renewable credentials stay in the OS credential store.
For connected Cloud presentations, the plugin pulls only a clean local workspace before editing and syncs validated edits afterward, unless you requested local-only or no-sync work. Publishing remains a separate request.
Create a Cloud presentation from local files
With vstd 0.5.0 and a Cloud service advertising workspace.create, build and
review a studio containing exactly one deck, then run:
vstd cloud login
vstd cloud workspace create --title "My presentation" --root ./my-presentation
The selected workspace from device approval owns the new presentation. The
command uploads canonical files, creates the first revision, and connects the
local directory for future pull and sync commands. It does not publish.
An uncertain response leaves a non-secret .vstd/cloud-create.json retry journal;
retry the same title and files, preserving the journal, to avoid duplicates.
Local-only authoring continues to work without an account.
File-backed editor-transform requests with delta: true support the canonical
128 MiB snapshot budget (16 MiB per file, at most 2,000 files). Inline snapshot
transforms retain their 20 MiB transport bound. Unchanged media is omitted from
delta responses.
Hosted audience elements use data-vstd-audience-share on their container,
data-vstd-audience-qr on an image, and data-vstd-audience-url on the URL text.
Cloud supplies the destination rather than copying a standalone public_host.
editor-transform accepts optional audience_url; release-build accepts
--audience-url HTTPS_URL. An explicitly empty value disables those elements,
while omission preserves standalone behavior. Hosted QR images are embedded PNGs,
so immutable releases require no sharing API or third-party image service.
Display images without changing source files
vstd image-delivery --input original.png --output display.webp creates a new
WebP using the same deterministic webp-v1-1920 recipe as optimized releases
(maximum width 1920, no enlargement, quality 82, alpha preserved). It requires
cwebp; PNG, JPEG, and WebP inputs are limited to 16 MiB and 40 million pixels.
The output must not already exist. JSON stdout records sourceSha256, sha256,
bytes, width, height, mediaType, and recipe. Hosted adapters may cache
this disposable display copy while retaining original bytes and logical paths
for editing. Authorization remains the host's responsibility.
In both editing and presentation mode, the player preloads all slide images, CSS backgrounds, and video posters after the visible slide's initial load. Background work uses two low-priority requests at a time and follows the current navigation position; visible images become eager/high priority immediately. Video streams retain their separate playback policy.
Hosted coordinators can read redesignRequests from the deck status endpoint. Each
item contains an engine-owned slide ID and fingerprint of actionable companion
requests. vstd agent --deck NAME --slide ID executes only that slide; it does not
start other queued edits. Cloud dispatch and billing remain control-plane owned.
Hosted callers may add --image-api https://BROKER/v1 --image-model gpt-image-1
to a selected sweep. The engine processes at most one matching image request,
registers its returned bytes in the normal library, and runs the landing pass.
Placement receives the exact generated asset ID, URL, and hash from the engine.
A sweep fails if the selected HTML still uses the previous image, only mentions
the new URL in a comment, or leaves the placement request unresolved; clearing
the companion alone cannot report successful image delivery.
Each pass includes the selected companion's content checkpoint in its opening
request. Planning and placement therefore have distinct request identities,
while repeating the same file state retains a stable identity for host replay checks.
The host supplies a scoped image capability through VSTD_OPENAI_KEY; no provider
key is required inside an isolated guest. Other slides' requests stay queued,
and failed generation is never retried inside the same sweep.
An ephemeral host job that already owns sandbox isolation and canonical result
integration may add --isolated-workspace to its selected sweep. Both agent
passes edit that job snapshot directly, avoiding redundant worktree/checkpoint
copies of its image library. This requires --deck and --slide and disables
Git push. Its worker is instructed to edit that isolated snapshot directly and
leave worktree creation, further agent dispatch and Cloud intake to the host.
Ordinary local sweeps keep their independent worktrees and recovery
checkpoints. The flag provides no isolation itself; its caller must supply it.
Interactive simulation bundles use a bounded browser archive cache and a bounded cache of verified unpacked files. Returning to a simulator slide checks current asset access with HEAD and reuses prepared files; reloading the deck reuses the archive without another ZIP transfer. SHA-256 and inventory checks still apply. Cloud gateways can advertise 4 MiB archive parts; large bundles then download four parts concurrently, with each part independently cacheable by the CDN. The assembled archive must pass the same SHA-256 check. Other servers retain ordinary delivery. Storage denial falls back to ordinary downloads. Leaving the slide destroys the application frame, so inactive simulations stop using CPU/GPU. Existing published releases retain their loader; publish a new release with vstd 0.7.32 to upgrade.
Cloud voice and release consumption
The engine player supports an authenticated host returning a GPT-Live transport
descriptor from POST /api/realtime/token. It then exchanges an ICE-complete SDP
offer plus the selected presenter context and local tool definitions through
POST /api/live/session. The host owns admission, model selection, provider
credentials and usage accounting. Both presenter voice and silent dictation use
Responses delegation; local Realtime sessions retain their existing protocol.
Hosted GPT-Live input transcripts open the speaker on the wake word independently of backend tool execution. The speaker starts muted, returns to mute on a stop-speaking request, and ignores assistant output transcripts for wake detection. Press V to start or stop Vessica's voice session; it starts listening silently. While connected, Shift+V toggles between conversation and silent listening, equivalent to saying “Vessica” and “that's all.” The spoken commands continue to work alongside the shortcut. In Logi Options+, configure the Spotlight 2 Action button's Single click → Keyboard shortcut → Shift+V. Keep the presenter player focused; voice shortcuts are ignored while typing in text fields. Shift+V does not start a microphone session when Vessica is off.
Wake and sleep control speech only: explicit navigation and highlighting commands execute while Vessica is connected, whether awake or silently listening.
Presenter voice reads the current page number and title from the player, using the same numbering as navigation (parked slides have no page number). It loads the selected slide's full companion Markdown and a presentation-wide table of contents with page numbers, titles, and topic summaries. Ask “what page are we on?”, “present this page”, or “go to the page about decision rights”. The agent reads fresh context before answering these questions or selecting a topic match; manual navigation, voice navigation, and companion saves refresh the context. Concurrent voice context reads wait for the latest selected-page refresh before updating the agent, preserving the companion during navigation and saved edits. GPT-Live receives a concise selection update as well as the backend context.
GET /api/deck/{deck}/voice-context?slide={id} returns current (page, title,
summary, full companion) and pages (ID, page, title, summary, hidden/parked
flags). It requires presenter access and is not cached. Hidden pages retain
their numbers; parked pages use 0. Companions are loaded through this API,
never embedded in exported presentation HTML. Exceptionally large context is
marked explicitly in the initial prompt and remains available in full through
the agent's get_presentation_context tool.
Playback is checked explicitly. If the browser blocks sound, click the Vessica
status pill to enable audio without ending the session. Hosts embedding the
presenter across origins must delegate autoplay alongside microphone permission.
A version bump merged to main publishes four immutable binaries and SHA256SUMS
only after the engine security and compatibility workflow passes. Existing
versions are never overwritten. Cloud consumes a released version and its exact
revision/checksums through engine.lock.json; a Cloud pin update deploys through
its own CI and GitHub-linked Railway services.
Hosted Live Responses lifecycle/function events are relayed through authenticated
GET /api/live/events; context updates use POST /api/live/context. Browser audio
continues directly over WebRTC. Cloud owns the backend model pin and billing.
Optional Vessica avatar overlay
Set LIVEAVATAR_API_KEY in the engine server environment to enable the optional
LiveAvatar overlay. Keep this provider key out of studio.yaml, decks and built
HTML. In the Presentation HUD, Avatar on/off selects video or voice-only
mode. The switch defaults off and remembers your choice in this browser across
presentations and sessions. Switching it off during conversation immediately
stops the renderer and restores direct voice; switching it on starts the avatar
if Vessica is already awake. If browser storage is unavailable, the selection
lasts for the current page only. Embedded hosts can retain it on a stable app
origin using vstd:avatar-preference messages with action: read (plus id) or
action: write (plus boolean enabled). Read replies use
vstd:avatar-preference-result, echo id, and include boolean enabled. Hosts
must check the exact editor origin and frame source. The player checks parent
source and the pending read ID, and ignores stale replies after a manual choice.
Voice-only mode uses the direct voice stream without loading the avatar SDK, creating an audio capture/worklet, allocating a provider session, or buffering audio for avatar synchronization. V starts Vessica silently listening; Shift+V and the Logitech Action button mapped to Shift+V wake/sleep conversation in either mode.
With Avatar on, no avatar session starts when a deck loads or while Vessica silently listens. Saying “Vessica”, pressing Shift+V, or the presenter conversation button starts a LITE session using Alessandra in Black Suit (portrait green-screen studio avatar). Sleep/“that's all”, voice shutdown and leaving the page stop the provider session and remove its video, audio and processing resources.
Video becomes visible as soon as the renderer video and control channel are
ready, even if its audio track arrives later. The avatar appears without a background in the lower-right corner. The browser
removes green pixels on a transparent canvas. Ordinary voice continues while the
avatar connects; synchronized LiveAvatar audio takes over at a pause to avoid
repeating speech. Missing configuration, blocked sound, SDK/network failure or
session expiry falls back to ordinary voice. Wake again to start another avatar
session. This optional feature loads the pinned livekit-client@2.22.4 browser
module from jsDelivr only on wake; core presentation and voice operation do not
require it.
The presenter-only, same-origin host contract is POST /api/avatar/start,
POST /api/avatar/keepalive and POST /api/avatar/stop, with JSON { "id": "<browser-generated UUID>" }. Start returns ws_url, livekit_url and a scoped
livekit_client_token; it never returns the provider API key or provider session
token. Hosts must scope IDs to the authenticated presenter, cancel late starts,
and explicitly stop the provider. A 15-second browser heartbeat refreshes a
45-second cleanup lease. Provider sessions have a 120-second hard cap, matching
the existing LiveAvatar prototype; expiry returns to ordinary voice. Active,
hidden or silent sessions still consume LiveAvatar credits (LITE: 1/minute).
A host stop failure is retried; the lease and provider cap bound abnormal exits.
Cloud owns its authenticated gateway, provider credentials and admission. Its avatar adapter must be enabled separately and the released engine pinned before this becomes available in Cloud. Static exports and audience views cannot start sessions. No Cloud credential is installed in an editing sandbox.
Offline avatar checks: go test ./internal/avatar ./internal/server ./internal/studio. The optional browser acceptance fixture uses Playwright:
VSTD_PLAYWRIGHT_MODULE=/absolute/path/to/node_modules/@playwright/test node internal/studio/testdata/avatar-browser.cjs. It generates its own green-screen
media and checks transparent pixels, lower-right placement, audio forwarding,
wake/sleep requests and teardown without consuming provider credits. It writes
a test-figure screenshot to /tmp/vessica-liveavatar-qa.png. Run
internal/studio/testdata/avatar-mode-browser.cjs with the same Playwright module
to verify the full player HUD switch and persistence across page sessions. Real avatar edge
quality, account access and end-to-end lip sync still require a provider trial.
AV handoff and private presenter controls
Vessica Studio Cloud can grant temporary, read-only AV access to one saved
revision and keep the owner's phone controller separate. The public engine
supports this through editor-transform with presentation_only: true: its
identity is presenter/read-only and every write is rejected. Voice exposes only
presentation tools, and attempted editing tools are refused before execution.
The shared player opts into the hosted controller transport only when its trusted
/api/me response includes remote_control: true, a screen name, revision and
show_expires_at. It reports actual slide, voice, microphone, playback and avatar
state to POST /api/presenter/poll; commands use monotonic sequence numbers and
receipts. Local operation remains offline-capable and does not use this transport.
POST /api/presenter/end closes the screen. The host owns authentication, fixed
revision storage, expiry, revocation, command admission and provider cleanup.
On the AV computer, use Enable show, allow microphone access, select the venue microphone, and check the system audio output. Fullscreen and audible playback remain subject to browser permission and local user gestures. Phone controls do not capture a microphone or play presentation audio. Rehearse voice, avatar, screen selection and venue routing before the show. A normal HID clicker continues to navigate with Page Up/Page Down.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
vstd
command
vstd — the Vessica Studio engine.
|
vstd — the Vessica Studio engine. |
|
internal
|
|
|
avatar
Package avatar manages optional LiveAvatar renderers.
|
Package avatar manages optional LiveAvatar renderers. |
|
bundle
Package bundle validates and registers bounded, immutable application ZIPs.
|
Package bundle validates and registers bounded, immutable application ZIPs. |
|
catalog
Package catalog owns personal presentation organization that is independent of deck ownership and sharing permissions.
|
Package catalog owns personal presentation organization that is independent of deck ownership and sharing permissions. |
|
chart
Package chart contains chart-authoring migrations that operate on the Studio file contract without introducing a charting runtime dependency.
|
Package chart contains chart-authoring migrations that operate on the Studio file contract without introducing a charting runtime dependency. |
|
chromium
Package chromium resolves the optional browser binary used by visual exports, source critics, and chart migration tools.
|
Package chromium resolves the optional browser binary used by visual exports, source critics, and chart migration tools. |
|
cloud
Package cloud implements the public, versioned Vessica Studio Cloud HTTP boundary.
|
Package cloud implements the public, versioned Vessica Studio Cloud HTTP boundary. |
|
cloudauth
Package cloudauth manages native cloud sessions without exposing renewable credentials to callers or persisting short-lived access tokens.
|
Package cloudauth manages native cloud sessions without exposing renewable credentials to callers or persisting short-lived access tokens. |
|
cloudpublish
Package cloudpublish selects synchronized revisions and orchestrates publication through the versioned cloud boundary.
|
Package cloudpublish selects synchronized revisions and orchestrates publication through the versioned cloud boundary. |
|
library
Package library owns the shared image and video asset catalog stored in a studio root's library/manifest.json.
|
Package library owns the shared image and video asset catalog stored in a studio root's library/manifest.json. |
|
oai
Package oai wraps the OpenAI HTTP APIs the engine needs: image generation (gpt-image-2) for the visual library, and ephemeral client-secret minting for gpt-realtime-2 presentation sessions.
|
Package oai wraps the OpenAI HTTP APIs the engine needs: image generation (gpt-image-2) for the visual library, and ephemeral client-secret minting for gpt-realtime-2 presentation sessions. |
|
reconcile
Package reconcile owns three-way presentation reconciliation for every writer.
|
Package reconcile owns three-way presentation reconciliation for every writer. |
|
s3
Package s3 is a minimal S3-compatible client for the video pipeline: PUT/HEAD objects (library sync) and presigned GET URLs (hosted serving).
|
Package s3 is a minimal S3-compatible client for the video pipeline: PUT/HEAD objects (library sync) and presigned GET URLs (hosted serving). |
|
server
Agent worker: executes queued redesign requests by running a headless coding-agent session inside the studio root.
|
Agent worker: executes queued redesign requests by running a headless coding-agent session inside the studio root. |
|
studio
Package studio implements the Vessica Studio content model: a root folder containing themes/, library/, requests/ and decks/, where each deck is a directory of slide fragments (NNNN-slug.html) paired with companion markdown files (NNNN-slug.md) carrying the research, evidence and talk track behind the slide.
|
Package studio implements the Vessica Studio content model: a root folder containing themes/, library/, requests/ and decks/, where each deck is a directory of slide fragments (NNNN-slug.html) paired with companion markdown files (NNNN-slug.md) carrying the research, evidence and talk track behind the slide. |
|
video
Package video implements the video asset pipeline: ingest (normalize with ffmpeg when available, extract a poster frame, probe metadata), the library manifest entries, and content-addressed storage under library/video/.
|
Package video implements the video asset pipeline: ingest (normalize with ffmpeg when available, extract a poster frame, probe metadata), the library manifest entries, and content-addressed storage under library/video/. |
|
Package plugin embeds the agent skill definitions so the vstd binary can serve them to any agent runtime (`vstd skill <name>`).
|
Package plugin embeds the agent skill definitions so the vstd binary can serve them to any agent runtime (`vstd skill <name>`). |