vm pkg es el manejador de paquetes y dependencias de VestaVM.
Disenado con anti-malware by design: blindado contra los vectores
de ataque historicos de npm, pip y cargo desde el primer dia.
Este manual cubre:
- Como crear un paquete propio.
- Como distribuirlo (mediante un repositorio git como GitHub, GitLab, Gitea o Codeberg -- modelo federado, sin registry central).
- Como consumir paquetes de otros.
- Como firmar y verificar integridad y autoria.
| Problema en npm/pip/cargo | Solucion en VestaVM |
|---|---|
Scripts postinstall ejecutan codigo arbitrario al instalar |
Paquetes son solo codigo Vesta + datos. Cero ejecucion en install. |
^1.2 floating sin lock fuerte |
vx.lock obligatorio con sha256 por dep. Sin sha256, falla cerrado. |
Typosquatting (raqquests vs requests) |
Trust pins por glob: [trust] "@vesta/*" = "kpub1:...". |
Cualquier paquete puede leer /etc/passwd |
Capabilities declaradas por paquete + grant explicito. Default deny-all. |
| Maintainer takeover silente | Author fingerprint check: cambio entre versiones produce error. |
| Transitive dep explosion anonima | vm pkg add muestra TODOS los autores transitivos antes de instalar. |
| Build scripts no sandboxed | Comptime macros corren en ComptimeRuntime con sandbox cap-restricted. |
Cada proyecto y cada paquete tiene un manifest. Formato dual:
vx.toml: canonico humano (recomendado).vx.json: equivalente machine-friendly para tooling.
El parser detecta el formato por extension. Si ambos coexisten en el
mismo proyecto, error explicito. Conversion entre ambos con
vm pkg convert toml o vm pkg convert json.
Auto-generado por vm pkg. Debe commitearse a git. Contiene los
hashes sha256 EXACTOS de cada dep resuelta y el commit/rev al
momento de la resolucion. Cualquier intento de instalar contenido
que no matchee estos hashes falla.
proyecto/
vx.toml # manifest
vx.lock # lockfile (commit-able)
.gitignore # auto-generado por vm pkg init
vx_modules/ # deps locales (estilo node_modules)
src/ # tu codigo
$VX_HOME/ # global per-usuario
cache/<sha256>/ # cache de tarballs descargados
packages/<name>/<version>/ # deps instaladas con install = "global"
keys/ # claves publicas y privadas
trust.toml # pins de autores confiables
Resolucion de $VX_HOME (en orden):
- Variable de entorno
VX_HOME(override explicito). - Per-usuario:
%APPDATA%\Vesta\en Windows,~/.vesta/en POSIX. - System-wide cuando la VM se instala como administrador.
vx_modules/ recibe el contenido descargado y verificado. Cualquier
modificacion local rompe el sha256 del lockfile y vm pkg verify
falla. No hay forma de meter codigo "extra" post-install --
diferencia critica con node_modules donde es comun parchear in-place.
Para hacer fork local de una dep: declarar en vx.toml con
path = "../mi-fork" (sin sha256/firma, asume confianza dev).
mkdir mi-paquete && cd mi-paquete
vm pkg init # interactivo: nombre, version, autorTe pregunta:
- Nombre del paquete: usar formato
@autor/nombrepara qualified (recomendado para evitar colisiones), o nombre plano si es interno. - Version: semver
MAJOR.MINOR.PATCH. - License: SPDX identifier (MIT, Apache-2.0, GPL-3.0, etc.).
- Autor: tu nombre o handle.
Resultado: vx.toml + .gitignore apropiado.
Para inicializar en JSON en vez de TOML:
vm pkg init --jsonmi-paquete/
vx.toml # manifest
vx.lock # lockfile (solo si tu paquete consume otras deps)
README.md # documentacion
LICENSE # texto de la license
src/
lib.vx # entry point publico del paquete
internal/
helpers.vx # codigo no-publico
tests/
test_lib.vx
examples/
demo.vx
El entry point convencional es src/lib.vx. Cuando alguien hace
import "@autor/mi-paquete";, ese fichero (y los que reexporte) es lo
visible.
// src/lib.vx
import "src/internal/helpers"; // privado por defecto
// Reexportar simbolos publicos:
public import "src/internal/helpers" only Counter, Result;
// O definir simbolos publicos directamente aqui:
public fn version() -> string { return "0.1.0"; }
Sin public, los simbolos son privados al paquete y no son visibles
para quien lo consuma. Ver Modulos.md para detalles del sistema de
modulos y reexport.
Si tu paquete necesita acceder a recursos, declaralo en el manifest:
[capabilities]
declared = ["fs:read=/tmp", "net=api.foo.com:443"]El consumer DEBE dar grant explicito a cada cap para que tu paquete pueda usarlas. Si NO declaras una cap pero la usas en runtime, el sandbox aborta.
Para libs low-level (FFI, kernel calls, drivers):
[capabilities]
unsafe = trueunsafe = true bypasa el sandbox completo. El consumer tiene que
poner trust_unsafe = true explicito en su [dependencies] para
aceptar la lib.
[cache]
analysis_unused_runs = 30El compilador guarda entre compilaciones no solo lo que CONCLUYE de un programa, sino el razonamiento con el que lo concluye -- def-use, rangos, points-to --, para no rehacerlo cada vez. Ese almacen va por funcion: si tocas una, se recalcula la suya y las demas se leen de disco.
Una entrada guardada no cae al primer descuido. Aguanta varias
compilaciones seguidas sin que nadie la pida, porque que un analisis no
haga falta hoy no significa que sobre: es normal que un consumidor no
llegue a correr en una compilacion concreta, y tirarlo por eso obliga a
recomputarlo la proxima. analysis_unused_runs es cuantas aguanta.
Por defecto 12, que va bien para casi todo. Se toca cuando el proyecto tiene un ritmo distinto:
- Subirlo en algo que se compila a rachas -- se trabaja una tarde y no se toca en semanas --: conserva mas trabajo hecho a cambio de sitio en disco.
- Bajarlo en algo que cambia constantemente y donde el espacio importa: libera antes, a cambio de recomputar mas.
En la duda, subirlo: perder trabajo ya hecho cuesta mas que guardarlo de mas, que es justo por lo que existe este almacen.
Es del paquete y no de quien compila, a proposito: quien sabe a que ritmo cambia una libreria es la libreria, no la maquina que la construye. Cada paquete lleva el suyo, y compilar uno no le impone su ajuste al siguiente.
vm pkg verify # check estructura, hashes locales
vm --vx src/lib.vx -o /tmp/test # confirma que compila standaloneLas firmas Ed25519 garantizan que el contenido que tus usuarios descargan es exactamente el que tu publicaste, y que viene de ti.
vm pkg keygen --out ~/.vesta/keys/private/mi_clave.pemOutput ejemplo:
ok: generando par Ed25519: OK
[OK] clave privada guardada en ~/.vesta/keys/private/mi_clave.pem (chmod 600 en POSIX)
fingerprint: kpub1:788c97df7d6dada3875ae024770f7860da349b988fc1fde4a65f0196e0053fc6
[INFO] comparte esta fingerprint con quienes vayan a verificar tus paquetes
Guarda la clave privada de forma segura. Si se compromete, cualquiera puede publicar paquetes con tu nombre. Si la pierdes, no puedes publicar updates firmados con tu identidad.
La fingerprint publica (kpub1:...) es lo que distribuyes a tus
usuarios para que pineen tu autoria.
vm pkg trust add kpub1:788c97df... --name mi_alias
vm pkg trust list # ver pins activos
vm pkg trust revoke kpub1:788c... # marcar como revokedVestaVM NO tiene registry central (por diseno: anti-takeover y federado). Se distribuye a traves de cualquier repositorio git publico: GitHub, GitLab, Gitea, Codeberg, o tu propio servidor.
Pasos tipicos (tu eliges el host):
# Inicializa el repo local
cd mi-paquete
git init
git add .
git commit -m "v0.1.0 inicial"
# Crea el remote en el host de tu eleccion (manualmente) y haz push:
git remote add origin <URL de tu repo>
git push -u origin main
# Crear un tag inmutable por version (RECOMENDADO):
git tag v0.1.0
git push --tagsRecomendaciones para distribucion segura:
- Usa tags inmutables (
v0.1.0) en lugar de branches. Una vez publicado un tag, no muevas el commit al que apunta. - Firma los tags con git (
git tag -s v0.1.0) o, mejor aun, publica la fingerprint Vesta (kpub1:...) en tu README para que los consumidores la pineen. - Si haces release nuevo con cambios incompatibles, sube major
(
v1.0.0 -> v2.0.0). El package manager respeta semver.
Pon en el README de tu repo:
## Verificar autoria
Esta libreria esta firmada con la clave Vesta:
kpub1:788c97df7d6dada3875ae024770f7860da349b988fc1fde4a65f0196e0053fc6
Para pinear como autor confiable:
vm pkg trust add kpub1:788c97df... --name mi_aliasAsi los consumidores pueden registrar tu fingerprint la primera vez que consumen tu lib, y cualquier release futuro firmado por otra clave sera rechazado automaticamente.
# Desde un repo git (tag inmutable, recomendado):
vm pkg add @autor/mi-paquete --git https://github.com/autor/mi-paquete --tag v0.1.0
# Desde un commit exacto (mas estricto, no se mueve nunca):
vm pkg add @autor/mi-paquete --git https://github.com/autor/mi-paquete --rev abc123de...
# Estilo Go (auto-resuelve el host como git URL):
vm pkg add github.com/autor/mi-paquete@v0.1.0
# Path local (desarrollo):
vm pkg add @me/utils --path ../utils-lib
# Dev-dependency (solo para tests, no se distribuye):
vm pkg add --dev @vesta/testCada vm pkg add muestra un audit transitivo antes de aplicar:
Agregando @autor/mi-paquete v0.1.0...
deps transitivas (3):
@autor/mi-paquete v0.1.0
autor: kpub1:788c... (no pinneado, primera vez)
unsafe: false
capabilities: fs:read=/tmp
@vesta/io 2.0.1
autor: kpub1:abc... (trusted via @vesta/*)
unsafe: false
capabilities: (ninguna)
@vesta/strings 1.0.5
autor: kpub1:abc... (trusted via @vesta/*)
unsafe: false
capabilities: (ninguna)
Continuar? [y/N]
Si algun paquete es unsafe = true, warning explicito:
ATENCION: @vendor/driver declara UNSAFE.
- Bypasara todo el sandbox.
- Fingerprint autor: kpub1:... (no pinneado)
Anade --trust-unsafe para confirmar.
vm pkg install # descarga TODO segun vx.lock
# verifica sha256 y firmas
# falla si algun hash no matcheaEsto descarga cada dep al vx_modules/ del proyecto, validando que
el contenido recibido (incluido el commit exacto si es git) produce
el sha256 declarado en vx.lock. Si alguna no matchea, aborta sin
copiar nada -- detecta tampering y cambios silenciosos.
// src/main.vx
import "@autor/mi-paquete";
fn main() -> i32 {
let v = autor::mi_paquete::version();
println("{v}");
return 0;
}
Ver Modulos.md para sintaxis completa de import, only, alias.
git clone <repo del proyecto>
cd <repo>
vm pkg install # descarga TODO segun vx.lock
# mismo bit-perfect que el del autorEl vx.lock commit-eado en su repo garantiza que descargas exactamente
lo mismo que ellos tenian.
vm pkg verifyOutput ejemplo:
Verificando integridad de paquetes
==================================
[OK] @vesta/io verificado
[OK] @vesta/buffer verificado
[OK] @autor/mi-paquete verificado
ok: 3
fail: 0
Recalcula el sha256 del contenido de cada dep en vx_modules/ y lo
compara con el declarado en vx.lock. Detecta tampering local,
cache corrupto o discrepancias post-merge en git.
vm pkg auditDetecta:
- Paquetes con
unsafe = trueen la cadena transitiva. - Paquetes sin firma o de autores no pinneados.
- Cambios de autor entre versiones (signal de posible takeover).
- Cambios de
sha256con la misma version (signal de tampering). - Capabilities expandidas vs la version anterior.
Output con severidades:
Auditoria de dependencias
==========================
[CRITICAL] @some/lib AUTHOR_CHANGED
el autor cambio entre 1.2.0 (kpub1:abc...) y 1.2.1 (kpub1:xyz...)
hint: posible takeover; verifica manualmente con el publisher original
[WARNING] @vendor/driver UNSAFE
el paquete se declara unsafe (acceso bajo nivel)
hint: revisa el codigo manualmente antes de install
Resumen: 1 critical 1 warnings
Exit code 2 cuando hay findings critical; 0 cuando todo limpio.
[package]
name = "@autor/mi-paquete" # @org/name (qualified) o nombre plano
version = "0.3.1"
authors = ["David"]
license = "MIT"
edition = 1 # version del schema del manifest
[capabilities]
declared = ["fs:read=/tmp", "net=api.foo.com:443"]
# unsafe = true # solo para libs low-level
[dependencies]
# git con tag (recomendado: inmutable si el autor no lo mueve):
"@vesta/buffer" = { git = "https://github.com/vesta/buffer", tag = "v1.2.3", sha256 = "..." }
# git con rev exacto (mas estricto):
"@vesta/http" = { git = "https://github.com/vesta/http", rev = "abc123de...", sha256 = "..." }
# git con branch (sigue HEAD; menos seguro, util en dev):
"@me/utils" = { git = "https://github.com/me/utils", branch = "develop", sha256 = "..." }
# path local (sin sha256/firma, asume confianza dev):
"@me/local" = { path = "../local-lib" }
# install global vs local (default = local en vx_modules/):
"@vesta/stdlib" = { version = "1.0", sha256 = "...", install = "global" }
# unsafe escape-hatch consumer-side:
"@vendor/driver" = { sha256 = "...", trust_unsafe = true, grant = ["ffi:call"] }
[trust]
"@vesta/*" = "kpub1:a1b2c3..." # cualquier lib bajo @vesta/ firmada por esta key
"@me/*" = "kpub1:d4e5f6..."
"exact/foo" = "kpub1:..." # match exacto
[scripts]
build = "vm --vx src/main.vx -o build/app"
test = "vm --run tests/run_all.velb"
fmt = "vm fmt src/"
[dev-dependencies]
"@vesta/test" = { git = "https://github.com/vesta/test", tag = "v0.1.0", sha256 = "..." }{
"package": {
"name": "@autor/mi-paquete",
"version": "0.3.1",
"authors": ["David"],
"license": "MIT",
"edition": 1
},
"capabilities": {
"declared": ["fs:read=/tmp", "net=api.foo.com:443"]
},
"dependencies": {
"@vesta/buffer": {
"git": "https://github.com/vesta/buffer",
"tag": "v1.2.3",
"sha256": "..."
}
},
"trust": {
"@vesta/*": "kpub1:a1b2c3..."
}
}Auto-generado. No editar a mano:
[meta]
version = "1"
generated_at = "2026-05-26T12:34:56Z"
resolver = "mvr-1"
[[package]]
name = "@vesta/buffer"
version = "1.2.3"
source = "git+https://github.com/vesta/buffer.git"
resolved_rev = "abc123de..." # commit hash exacto resuelto
sha256 = "deadbeef..." # hash del contenido descargado
author_fp = "kpub1:..."
signature_alg = "ed25519"
unsafe = false
declared_caps = ["fs:read=/tmp"]
deps = ["@vesta/io"]
install_at = "project"Minimum Version Resolution (MVR), estilo Go modules. Deterministico, sin SAT solver.
Reglas:
- Cada dep declara una version semver minima (
^1.2significa >= 1.2.0, < 2.0.0). - Si varias deps piden la misma lib con versiones distintas, se elige la MAYOR de las minimas (la unica que satisface a todas).
- Si dos versiones de la misma lib se requieren en diferentes mayores (uno pide 1.x, otro pide 2.x con breaking changes), ambas conviven en el binario final con namespacing automatico.
- Conflictos de tipos cross-version los reporta el type checker en compile time, no el package manager.
Especificadores semver soportados en [dependencies]:
"1.2.3"-- exact."^1.2"-- >= 1.2.0, < 2.0.0 (caret, default semver)."~1.2.3"-- >= 1.2.3, < 1.3.0 (tilde).">=1.0"-- mayor o igual.">1.0"-- mayor estricto."*"o"latest"-- ultima disponible.
mkdir mi-app && cd mi-app
vm pkg init
vm pkg add github.com/autor/lib@v1.0.0
vm pkg install
vm --vx src/main.vx -o build/app# en mi-app/vx.toml
[dependencies]
"@me/utils" = { path = "../utils-lib" }vm pkg installCrea symlink/copia desde ../utils-lib; cualquier edicion se refleja
inmediatamente.
vm pkg add @sospechoso/foo --git https://github.com/sospechoso/foo --tag v1.0.0
# Lee el audit antes de confirmar con 'y'.
# Si algo huele mal, responde 'N'.vm pkg update @vesta/buffer # solo esa dep
vm pkg verify # check integridad post-update
vm pkg audit # signals de takeover/tamperinggit clone <repo>
cd <repo>
vm pkg install # exactamente lo que el autor teniaToda dep no-local DEBE tener sha256 en el lockfile. Si falta,
vm pkg install falla con error claro. Cualquier mismatch entre
contenido descargado y sha256 declarado aborta el install
ANTES de copiar nada a vx_modules/.
Cada paquete declara [capabilities] en su manifest. El consumer
SOLO da las caps que aparecen en grant de su [dependencies].
Regla critica: grant ⊆ declared. El consumer puede dar MENOS
de lo que el paquete declara, pero NUNCA mas. Si el paquete intenta
usar una cap fuera de su grant, error runtime.
Cross-package, las caps se AND-en transitivamente: si el root no
tiene fs:read, ninguna dep transitiva la obtiene aunque el grant
directo la incluya.
Cada paquete (excepto deps con path = "...") debe estar firmado con
Ed25519. El fingerprint = SHA-256 del pubkey en 64 hex chars,
prefijado por kpub1:.
vm pkg verify rechaza paquetes con:
- Firma corrupta o ausente.
- Fingerprint que no matchea con
[trust]. - Cambio de autor entre versiones.
A diferencia de npm, el manager NO ejecuta scripts del paquete descargado. Si un paquete necesita "build steps":
- Codigo runtime regular: el consumer lo compila como cualquier modulo Vesta.
- Codigo comptime: usar
@Macroque corre enComptimeRuntimecon sandbox cap-restricted. VerMetaprogramacion.md.
Esto cierra el vector de ataque mas comun de npm.
Las caps se AND-en cross-package automaticamente. Si un paquete
malicioso intenta cargar otro con mas caps que las suyas via
loadmodule, el loader rechaza. Ver Sandbox.md.
vm pkg add / update / remove muestra:
- Autores transitivos y sus trust status.
- Paquetes unsafe.
- Capabilities cambiadas.
- Cambios de autor.
El usuario confirma con y antes de aplicar.
El cache global ($VX_HOME/cache/<sha256>/) es content-addressed:
la clave es el hash. Cualquier corrupcion del cache se detecta al
re-verificar el hash en el siguiente vm pkg install.
| Comando | Descripcion |
|---|---|
vm pkg init [--json] |
Crea vx.toml o vx.json + .gitignore |
vm pkg add <spec> [--dev] |
Agrega dep + actualiza lock + audit |
vm pkg remove <name> |
Quita dep + actualiza lock |
vm pkg install |
Descarga + verifica todo segun lock |
vm pkg update [name] |
Re-resuelve deps segun reglas semver |
vm pkg list [--tree] |
Lista deps directas o arbol completo |
vm pkg verify |
Check sha256 + firmas vs vx.lock |
vm pkg audit |
Auditoria de seguridad transitiva |
vm pkg convert <toml|json> |
Convierte formato del manifest |
vm pkg keygen [--out PATH] |
Genera par Ed25519 + fingerprint |
vm pkg trust list |
Lista pins de autores confiables |
vm pkg trust add <fp> [--name N] |
Pinea un autor |
vm pkg trust revoke <fp> |
Marca pin como revoked |
vm pkg run <script> |
Ejecuta entry de [scripts] |
- Modulos.md -- sistema de modulos
import. - Sandbox.md -- modelo capability-based.
- CargaDinamica.md --
loadmoduley hot reload. - Metaprogramacion.md --
@MacroyComptimeRuntime.