El reparto completo, de principio a fin
Un ejemplo real llevado hasta el final: una aplicación con cuatro ventanas, sus permisos repartidos, y el recorrido de decisiones que llevó a cada línea.
La aplicación
Un catálogo de problemas de programación —el mismo caso que el desktop/ de este monorepo—, con cuatro ventanas:
| ventana | etiqueta | qué hace |
|---|---|---|
| principal | main |
lista los problemas, abre uno, sincroniza el catálogo |
| ajustes | settings |
un formulario de preferencias |
| documento | doc-* |
muestra y edita un problema |
| vista previa | preview |
renderiza el enunciado en formato enriquecido, que viene del catálogo |
La cuarta es la interesante: el enunciado no lo escribiste tú. Viene de un catálogo que se sincroniza, así que es contenido ajeno y merece el trato del nivel 28.2.
El reparto
// capabilities/base.json — el suelo, para todas{ "identifier": "base", "description": "Lo mínimo para que una ventana pueda invocar y escuchar. Sin esto no hay puente.", "windows": ["main", "settings", "doc-*"], "permissions": ["core:default"]}Fíjate en que preview no está. No es un olvido: esa ventana no debe poder invocar nada, ni siquiera lo básico. Es la decisión más importante del fichero.
// capabilities/principal.json{ "identifier": "principal", "description": "La ventana principal abre proyectos que el usuario elige, y lanza la sincronización del catálogo", "windows": ["main"], "platforms": ["macOS", "windows", "linux"], "permissions": [ "dialog:allow-open", "opener:allow-open-url", { "identifier": "fs:allow-read", "allow": [{ "path": "$APPDATA/catalogo/**" }] } ]}Cuatro decisiones ahí:
dialog:allow-openy no el conjunto por defecto, que además concedería guardar. La principal no guarda con diálogo.opener:allow-open-urlacotado, para abrir la documentación en el navegador. El nivel 30 le pondrá ámbito de dominio.- Lectura con ámbito al directorio del catálogo, no al de datos entero.
- Plataformas de escritorio, porque esta ventana no existe en móvil.
// capabilities/ajustes.json{ "identifier": "ajustes", "description": "Un formulario. Solo necesita leer y escribir preferencias.", "windows": ["settings"], "permissions": ["store:allow-get", "store:allow-set", "store:allow-save"]}Tres permisos, y ninguno de ficheros. Es la ventana que más se beneficia del reparto: con una capability global tendría diálogos, lectura de disco y apertura de enlaces sin necesitar nada de eso.
// capabilities/documentos.json{ "identifier": "documentos", "description": "Cada ventana de documento lee y escribe SU problema, nada más", "windows": ["doc-*"], "permissions": [ { "identifier": "fs:allow-read", "allow": [{ "path": "$APPDATA/catalogo/problemas/**" }] }, { "identifier": "fs:allow-write", "allow": [{ "path": "$APPDATA/catalogo/problemas/**" }] } ]}Una decisión discutible y deliberada: el ámbito es todos los problemas, no el suyo. Acotarlo a uno exigiría una capability por ventana generada en tiempo de ejecución, cosa que el modelo declarativo no hace. La alternativa correcta cuando eso importa es la del nivel 14.4: que la escritura no pase por el plugin de ficheros sino por un comando propio que resuelva la ruta desde el identificador del documento. Aquí se acepta el ámbito ancho porque el contenido es del propio usuario.
// capabilities/vista-previa.json → NO EXISTEY ésta es la lección del nivel: la ventana de vista previa no tiene fichero. No hay nada que conceder. Si el enunciado que renderiza lleva algo malicioso, se ejecuta en una webview sin ninguna vía hacia el core.
Lo que la vista previa sí puede hacer
Porque tiene que mostrar algo, y ahí está el diseño:
Con un matiz importante que el nivel 31 desarrolla: esa ventana sigue pudiendo hacer peticiones de red desde su JavaScript si la política de contenido lo permite. Quitarle las capabilities cierra la puerta al core y no la de la red — y por eso la política de contenido es el complemento obligatorio.
La comprobación
El reparto se sostiene con cuatro afirmaciones, en la prueba del nivel 28.4:
main puede abrir un diálogo ✓settings NO puede abrir un diálogo ✓ ← la que importadoc-1 puede escribir en problemas ✓preview NO puede invocar nada ✓ ← la que más importaLas dos negativas son las que impiden que el reparto se relaje en seis meses sin que nadie se entere.