VAM-PIP is a Linux-first package manager for large Virt-A-Mate libraries. It
keeps the complete .var collection on disk while exposing a much smaller,
dependency-closed set to VaM.
The main interface is a local web app where you can browse the catalogue built by BrowserAssist, pin packages that must always remain available, and enable a scene or preset temporarily—three days by default.
VAM-PIP does not delete or repack archives. Managed mode changes visibility with same-directory renames:
Creator.Package.1.var
Creator.Package.1.var.vampip-disabled
Every switch is journalled, the pre-manager state is saved as a baseline, and leaving managed mode restores that baseline.
Version 0.18.0 is functional but should still be treated as an early release. Package switching is deliberately conservative:
- entering managed mode requires explicit confirmation;
- invalid archives are reported and left untouched;
- packages are never disabled while VaM is running;
- package-backed live loads rescan VaM before resolving their virtual paths;
- ambiguous same-ID copies are compared by logical archive contents, so harmless ZIP repacks are accepted while conflicting member data is refused;
- a failed multi-file switch rolls back completed renames automatically;
- every applied switch has a recovery manifest.
A backup of an important VaM installation is still recommended.
- Linux
- Python 3.10 or newer
- a local filesystem supporting Linux
renameat2(RENAME_NOREPLACE) - a VaM installation with an
AddonPackagesdirectory - BrowserAssist for in-game-style resource browsing (optional)
- Proton only if you want VAM-PIP to invoke a Proton launch script
The manager itself has no third-party Python dependencies.
Run directly from the checkout:
./vampip manager configure /path/to/VaM/AddonPackages
./vampip manager scan
./vampip-managervampip-manager opens a browser on a private loopback URL. The URL contains a
per-install write token in its fragment; keep the terminal running while using
the interface.
Package management works without BrowserAssist. To add the searchable scene/preset/item catalogue, install BrowserAssist, let it build or refresh its local data, then run:
./vampip manager catalog importIf no supported BrowserAssist data is available, the import command reports that explicitly and leaves the package inventory usable.
Package cards use BrowserAssist's indexed resource associations for their
preview collage, count, and type summary. Opening Browse contents then
checks those entries against the selected physical .var, so stale catalogue
rows and resources belonging only to another same-ID copy are not presented as
installed contents.
Alternatively, install both command-line entry points:
python3 -m pip install -e .
vampip-managerThe checked-in launchers store their state in this repository's ignored
.vampip/ directory. An installed command defaults to
${XDG_STATE_HOME:-~/.local/state}/vampip. Override either form with:
export VAMPIP_ADDON_DIR=/path/to/VaM/AddonPackages
export VAMPIP_STATE_DIR=/path/to/vampip-state- Scan packages and, if BrowserAssist is installed, import its catalogue.
- Review the detected default session plugins in the web app or with
./vampip manager session-plugins list. - Pin any additional package that every session needs but that is not in VaM's default Session Plugins preset.
- Review the managed-mode confirmation carefully.
- Start managed mode. VAM-PIP records the exact current enabled/disabled state before applying the smaller set.
- Find a scene or preset and select Enable for 3 days.
- Launch VaM, or load the resource in an already-running VaM after the bridge reports a successful rescan.
VAM-PIP reads
<VaM>/Custom/PluginPresets/Plugins_UserDefaults.vap. On the first applied
managed-mode activation, every enabled packaged plugin in that preset is
resolved with its dependencies and saved as a permanent pin. Enabled loose
scripts already live outside AddonPackages, so they remain available without
a package pin. A missing preset is treated as an empty default; a malformed
preset or an enabled packaged reference that cannot resolve blocks activation
before package visibility changes.
Selecting a packaged resource creates a lease containing its archive, declared
.var dependencies, and package references found in supported scene/preset
text. The lease stores exact resolved package versions, so a later catalogue
change does not silently alter an active lease. It also records the exact
catalogue resource and archive member. VAM-PIP will not change a same-ID
content choice used by an active resource lease, because package-only
dependency data cannot prove that another fork contains every referenced
member. Release the lease, choose the content, and load the resource again.
When BrowserAssist refreshes while managed packages are hidden, its new snapshot omits many resources it cannot currently see. VAM-PIP preserves the last-good catalogue rows for exact installed hidden package versions, while still removing stale local, uninstalled, invalid, and active-package entries. This keeps hidden looks and hair searchable without treating every old row as permanent.
The running manager fingerprints the package directory recursively. Adding,
replacing, renaming, or removing a .var causes the next status refresh or
15-second monitor pass to run the incremental inventory scanner; an unchanged
library does not trigger a full scan. If VaM is open and a newly discovered
archive is already enabled, the bridge receives one core package rescan.
For an already indexed BrowserAssist resource, VAM-PIP can resolve a newer installed package version even while BrowserAssist's version list is stale. It opens candidate archives and accepts the newer version only when the exact resource member is present. The resource card then shows the selected and newer versions, such as v2 → v4, and provides an explicit Update to v4 action. Ordinary Load continues to use the displayed current version; Update pins dependency discovery, the lease, and any live bridge action to the exact newer version. Entirely new resources still require BrowserAssist to rebuild its manifest before VAM-PIP can import their metadata.
No dependency scanner can identify packages loaded dynamically by every third-party script. Pin known runtime plugins that a collection always needs.
VAM-PIP can enable packages while VaM is open, but it never hides an archive that the process may still be using. Lease expiry and unpin operations therefore show as pending disables until VaM exits.
The web app polls a lightweight live-activity endpoint independently of the inventory lock. When VaM closes and automatic reconciliation begins, the banner changes to Hiding X of Y packages, shows progress, keeps the real VaM process badge current, and disables launch until the switch finishes.
Install the optional session bridge:
./vampip manager bridge installTo replace an older VAM-PIP bridge with protocol 2:
./vampip manager bridge install --forceThen, once in VaM:
- open Session Plugins;
- add
Custom/Scripts/VAMPip/Bridge/VAMPipBridge.cslist; - save it in the default session-plugin preset.
The bridge is a loose script, so VAM-PIP detects it in that preset but does not create a package pin for it.
The bridge polls a local mailbox, coalesces compatible rescan requests, serializes live actions, waits while VaM is loading, and invokes VaM's core package rescan. BrowserAssist must be reloaded when it needs to rebuild its private package/resource manifest. The bridge cannot rename files, run commands, accept operating-system paths, or invoke arbitrary VaM storables.
The Timeline tab discovers every VamTimeline.AtomPlugin in the current
scene and provides a large transport workspace plus a compact detachable
controller. Unmodified Timeline instances support a fixed legacy subset: play,
pause, stop/reset, frame stepping, animation choice, seek, speed, weight, and
lock where those storables are present.
The
enhanced Timeline interoperability branch
adds protocol 1 state for exact segment, layer, and clip synchronization. Its
audited drop-in workflow keeps each original
AcidBubbles.Timeline.<version> filename, script path, plugin class, and scene
slot, so existing embedded Timeline data loads normally without migration or
scene rewriting. It publishes bounded catalogues with separate catalog and
live-state revisions; the browser receives only bridge-minted opaque IDs. Every
command is checked against the observed revision and a fixed operation
allowlist. Atom UIDs, plugin IDs, animation labels, and VaM storable/action
names never cross the browser control boundary.
With VaM and the VAM-PIP manager stopped, install the enhanced adapter from a checkout beside VAM-PIP:
git clone --branch vampip-interop https://github.com/ethanfel/vam-timeline.git
cd vam-timeline
./timeline-dropin.sh status
./timeline-dropin.sh installThe installer accepts only audited whole-archive hashes, backs up each original
outside AddonPackages, preserves active/hidden filenames, and refuses unknown
or modified builds. To restore the byte-identical originals while VaM and the
manager are stopped:
./timeline-dropin.sh restoreTimeline state normally refreshes once per second and immediately after a control. Clip catalogues share a 1,024-entry global budget, prioritized for the selected atom and then playing instances. Transport stays live even when an instance receives no catalogue allocation, while published counts, limits, truncation flags, and sanitized adapter errors explain the bounded state.
The full view includes selectors, clip structure, inspector, interpolated playhead, and a bounded canvas dope-sheet foundation. Track/keyframe rendering activates when a future adapter version publishes graph data; keyframe mutation is intentionally not part of protocol 1. The compact route is:
/?view=timeline&popout=compact
Its title is VAM-PIP Timeline, which can be matched by a desktop-compositor
“keep above” rule. A browser popup cannot enforce always-on-top by itself.
The SAM 3D tab accepts one image and an explicit person bounding box, then runs Meta's native SAM 3D Body code in a dedicated Python 3.11 worker. The result can be retargeted onto an existing VaM Person, applied with one-step undo, and captured through a compatible VRRenderer camera atom.
Pose application and undo run as reversible VaM physics transactions. The 19
driven Person controllers and camera are frozen for the authoritative write.
All Person controllers then regain their saved physics state for an initial
settlement window, after which only headControl is held kinematic at its
settled position with the requested head rotation; the camera remains locked.
Person collision is restored immediately after the write. Undo restores the
saved transforms and physics settings, while failed Apply and bridge unload
restore every saved physics state. Captures are kept as a bounded per-job
history; existing VAM-PIP images in the legacy renderer directory are
backfilled automatically.
The workspace exposes both official DINOv3-H+ and original ViT-H checkpoints. A run records its immutable model identity, and Compare both creates a linked pair from the same source inputs. The single GPU queue loads them serially in separate worker processes, so they never occupy VRAM together. DINOv3-H+ remains the default and uses a pinned local DINOv3 source checkout. The worker is isolated from ComfyUI, and bridge installation includes the fixed camera preset and renderer scripts needed by that workflow. See the standalone SAM 3D Body setup for environment, model, and configuration instructions.
Completed reconstructions now use two independent workspaces. Morph owns an ordered reference gallery of up to eight completed bodies. Its Structure layer combines neutral skeleton measurements with bounded outlier rejection. Its separately optional Body Shape layer measures the neutral mesh silhouette for breasts, waist, hips, glutes, and upper thighs, then calibrates five allowlisted built-in VaM morphs against the selected Person without first changing that Person. Each layer has independent regions and fit strength. The bridge prepares that live neutral-mesh calibration incrementally, with one contour step per frame, and the workspace shows its preparing state. Structure can still be analyzed and reviewed while it warms, but Apply waits for the stable cache so the exact undo cannot be bound to a stale mesh. Pose + camera keeps its own current reconstruction and body selection, so choosing another pose image does not replace, reset, or re-analyze the Morph reference set.
Morph can also place its full source image on a tool-owned, unlit image panel
inside the VaM scene. The immutable copy is staged below
Custom/Images/VAMPip/SAM3D, rather than /tmp, and can be updated or hidden
from the workspace. Keep reference aligned is enabled by default in
Pose + camera: applying a pose moves the panel with the inferred camera so
the Person can be compared against the original frame. Pose undo restores the
panel together with the Person and camera.
Applying Structure and Body Shape creates one exact Person-wide undo snapshot and one atomic bridge request; another fit cannot replace that snapshot until it is restored. Body Scale, face morphs, materials, and breast/glute soft-body physics are not changed. Leg and torso length morphs can still alter final height. If an allowlisted shape morph is unavailable, the workspace reports the region instead of enabling an arbitrary package morph.
Named Person profiles stay in browser-local storage. They remember selected Structure and Body Shape regions, both strengths, and the independent Morph reference set, but never store live VaM morph identifiers, values, or revision tokens. Existing profiles are imported with Body Shape disabled, so an upgrade cannot silently change soft anatomy. Reopening a profile always requires a fresh read-only analysis against the current Person before Apply body fit becomes available.
Jobs created before neutral-body signatures were added remain valid as a single Structure reference and are marked Legacy · solo only. Their persisted SAM arrays can still produce a cached CPU-only Body Shape sidecar without rerunning inference or changing the job revision. Rerun an older image with the current worker before combining it with other views for Structure.
The Workspace tab is a capability-driven companion to VaM's asset UI. It organizes the complete imported catalogue into Scenes, SubScenes, atom presets, Custom Unity Assets, plugins, clothing, and all Person preset families. Every category says whether it is:
- browseable from the offline catalogue;
- loadable through the current bridge;
- waiting for a typed live-state implementation.
This version can load or merge a Scene; list, select, and create allowlisted
native atoms; replace or merge matching non-Person atom presets; and load a
SubScene into a matching existing or newly created SubScene atom. It can
also load raw Custom Unity Asset bundles into an existing or new
CustomUnityAsset, expose VaM's bounded contained-asset choices, add/select a
Person, and replace or merge all eleven native Person preset families:
Appearance, Animation, Breast Physics, Clothing, General, Glute Physics, Hair,
Morphs, Person Plugins, Pose, and Skin. It can also wear or remove individual
female and male clothing items on a selected Person. The Person workspace
presents that live wardrobe as a character sheet with explicit multi-item
sections for tops, bras, panties and underwear, full outfits, bottoms,
stockings, shoes, high heels, accessories, and other regions. The sections
organize the display but never impose one-item equipment slots. Clothing that
VaM reports without a safe catalogue identity remains visible as a read-only
in-game item instead of disappearing.
The Hair category swaps the wardrobe for a Hair Studio showing every bounded active layer and whether it uses VaM hair simulation. Exact unlocked layers can be disabled individually; locked, ambiguous, and truncated state fails closed instead of guessing. The inspector groups future Hair controls into style, material, simulation, and scalp areas without inventing current values. Other preset families use compact recipe views, because VaM does not publish a reliable single “currently equipped preset” for them.
Replacing a Scene requires explicit confirmation in both the browser and API. General and Person Plugin presets, non-Person atom presets, SubScenes, and raw Custom Unity Asset bundles also require a separate critical-action confirmation because their contents can replace broad state or carry executable or otherwise active Unity content.
For every live resource action VAM-PIP:
- resolves the numeric catalogue ID to an exact installed resource;
- creates a three-day dependency-closed lease;
- enables required packages without hiding anything from the running game;
- asks VaM to rescan and performs one statically allowlisted operation.
The browser never submits a resource path, storable name, atom type, or action name. It submits the server-owned category ID for standalone atom creation and a numeric resource ID for every load. Preset categories whose atom type is not in the audited native allowlist remain browse-only.
“Create new” is also enforced inside the VaM bridge: if that UID becomes occupied before the request executes, the load fails instead of changing the newly appeared atom.
For direct Custom Unity Asset loads, the bridge forces VaM's loadDll option
off immediately before assigning the bundle URL and never accepts that option
from the browser. This prevents a new sibling DLL from being loaded by the
operation, but cannot unload code already active in the VaM session.
Single-choice bundles load immediately; multi-choice bundles expose a
bridge-issued, stale-safe numeric picker without accepting a raw asset name.
Individual clothing uses an equally narrow desired-state contract. The
browser submits only a numeric catalogue ID, target Person UID, Wear or
Remove, the revision it observed, and an optional lease duration. The
manager resolves the catalogue ID to the exact installed, package-qualified
.vam resource reference; a BrowserAssist clothing UID is display metadata,
not action identity. VaM publishes a bounded presentation roster, validated
worn and locked references, and the Person's current gender. Only validated
references become actionable catalogue cards; opaque or built-in items never
gain a guessed removal action. The manager and bridge reject stale revisions,
incompatible wear requests, and locked changes. If bounded publication is
truncated, the workspace treats an unpublished item's state as unknown and
disables its action.
Selecting a resource card preview opens a centered inspector with a larger
preview, package and version details, the same owner actions as the compact
card, a lazy Dependencies catalogue, and a bounded Styles & variants
gallery. Dependency details scan the selected resource for direct package
references, follow installed meta.json dependencies transitively, label
active, hidden, and missing packages, and paginate the result inside the
inspector. Open package moves to the exact package in the Packages view.
The graph is bounded and cycle-safe; it is loaded only when the inspector
opens, so ordinary catalogue pages remain inexpensive.
When physical VARs share an exact ID but contain different logical data, the inspector compares their relative locations, logical fingerprints, active state, and declared dependencies. VAM-PIP never guesses which fork is correct. Use this content stores an explicit choice by logical digest, then applies that choice consistently to dependency resolution, archive previews, thumbnails, activation, and loading. The choice survives path moves and enabled/hidden suffix changes. If another fork is already active while VaM is running, the choice is saved but the switch waits until VaM closes.
The styles release covers
clothing items with same-package Clothing Item Presets, plus same-type
Preset Hair, Preset Clothing, and Clothing Item Presets families. A
relationship requires the exact creator, package, case-sensitive folder,
overlapping declared versions, and an actual base filename followed by a clear
separator. Same-type families require that separator and an exact atom type.
Clothing-to-item-preset matching deliberately also accepts an exact stem and
ignores atom type because the different resource types establish the roles
while BrowserAssist normally leaves the preset atom type empty. The longest
matching base owns each option. Numeric IDs and case-insensitive logical paths
are deduplicated before the inspector reports the full option count and
returns at most twelve previews. These are explicitly filename-based browsing
hints, not semantic or action identities. Child tiles show their own version
and package state, but remain browse-only until VAM-PIP can resolve a safe
target-specific action.
Raw plugins remain browseable but action-disabled. They execute code, and the BrowserAssist catalogue mixes entry scripts with helper source files. Trusted plugin entry-point classification and explicit code-loading confirmation are the next workspace target, followed by save/export workflows and then bounded live controls.
See the external workspace map and the Person-specific capability map for the exact implemented and planned surfaces.
See bridge/vam/README.md for the protocol and manual installation layout.
The Launch VaM button and this command:
./vampip manager launchuse launch-vam-desktop-proton.sh from the VaM root. When managed mode is
active, VAM-PIP reconciles the current pin and lease set before starting it.
The launcher must be executable and must remain inside the VaM directory.
Output is written to VaM/logs/vampip-launch.log.
VAM-PIP intentionally does not guess global Proton, Gamescope, Wayland, or resolution options. Keep those settings in the VaM-scoped launch script.
The browser uses the same service exposed by vampip manager:
# State and inventory
./vampip manager status
./vampip manager scan
./vampip manager packages Timeline --state active
# BrowserAssist catalogue
./vampip manager catalog import
./vampip manager resources "my scene" --type Scene --state hidden
# Permanent base set
./vampip manager pin AcidBubbles.Timeline
./vampip manager unpin AcidBubbles.Timeline
# VaM default Session Plugins preset
./vampip manager session-plugins list
./vampip manager session-plugins import
./vampip manager session-plugins import --include-disabled
# Temporary package access
./vampip manager lease Creator.Scene.4 --days 3
./vampip manager renew LEASE_ID --days 3
./vampip manager release LEASE_ID
# Preview first, then apply
./vampip manager reconcile
./vampip manager reconcile --apply --activate
# Restore the exact state captured before activation
./vampip manager deactivate
./vampip manager deactivate --applyMutating CLI commands either require --apply or clearly identify themselves
as immediate pin/lease changes. Run ./vampip manager --help for the complete
command tree.
session-plugins import immediately creates any missing pins. By default it
imports only enabled packaged defaults; --include-disabled is the explicit
opt-in for disabled entries. Add --apply to reconcile the enlarged pin set
immediately when managed mode is already active. Pin creation and reconciliation
share the manager lock; if reconciliation fails, the result reports
reconcile_error and leaves the successfully imported pins in place for a
later retry (the CLI also exits nonzero). The web app exposes the same
enabled-only operation as Import session defaults and performs equivalent
preservation automatically as part of first activation.
The original conservative package-maintenance CLI remains available alongside the manager:
./vampip scan
./vampip stats
./vampip doctor
./vampip duplicates --verify
./vampip prune
./vampip content-audit --min-mib 1 --verboseprune is a dry run unless --apply is supplied, and applied candidates move
to a timestamped quarantine rather than being deleted. Old versions are
reported but are not automatically discarded because scenes and plugins can
pin an exact version.
scan also rejects ZIP entries whose version-required high byte is nonzero.
Python can read those entries, but VaM's old SharpZipLib reports the combined
value (for example, 788 for 0x0314) as unsupported. Run
./vampip doctor --refresh to identify the archive and entry before loading it
in VaM.
Profiles are still useful for fixed, named package sets:
./vampip profile create my-scene Creator.Scene.4 AcidBubbles.Timeline
./vampip profile show my-scene --packages
./vampip profile activate my-scene
./vampip profile activate my-scene --applyThe safest normal recovery is:
./vampip manager deactivate
./vampip manager deactivate --applyThis restores each package to the enabled state recorded before managed mode. It also preserves packages that were already disabled.
Each applied manager switch writes JSON under:
<state-dir>/manager-runs/
To inspect and reverse one specific switch:
./vampip manager rollback /path/to/manager-runs/MANIFEST.json
./vampip manager rollback /path/to/manager-runs/MANIFEST.json --applyThe first command is read-only and reports the recorded state against the actual source/target paths. New format-2 switches use a small append-only progress sidecar, so a large package set no longer rewrites a multi-megabyte manifest after every filename change. Rollback preflights every archive and uses atomic no-clobber renames.
Do not rename the same managed archives manually while applying or rolling back a switch. VAM-PIP refuses to overwrite an existing target.
The manager is daemon-optional: the CLI and loopback web server call the same service layer and SQLite state. Package inventory scans are incremental, so unchanged archives are not reopened. The BrowserAssist import is transactional; a failed refresh retains the last usable generation.
The web server binds only to 127.0.0.1/localhost, authenticates API access,
checks mutation origins and Host headers, and serves no third-party assets.
More detail:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python -m unittest -v
node --check src/vampip/webui/app.jsThe package data configuration includes the web UI and bridge source in wheels and editable installs.
VAM-PIP's original code is licensed under the GNU General Public License v3.0 only.
Bundled third-party components remain under their original licenses. In
particular, src/vampip/renderer_assets/VRRendererX/ contains work licensed
under CC BY-SA 2.0 and MIT; its license notices are included alongside that
component.