The app manifest, cartolex-manifest/1¶
The interface starts from one request: GET /api/app/manifest. The shell reads
it, loads the catalogues of the interface language, imports the extensions’
modules (each register(api)), renders the navigation and routes. The manifest
is the contract between the app and any interface built on it.
{
"format": "cartolex-manifest/1",
"app": {"id": "cartolex", "name": "cartolex", "version": "1.0.2",
"build": {"commit": "3f2a1c9e…", "date": "2026-10-06"}, "platform": "Linux 6.8.0 x86_64"},
"branding": {"name": "cartolex", "logo": "/static/brand/logo.svg", "accent": null},
"locales": {"available": ["en", "fr", "pt-BR"], "default": "en",
"catalogues": {"en": ["/static/i18n/en.json"], "fr": ["/static/i18n/fr.json"],
"pt-BR": ["/static/i18n/pt-BR.json"]}},
"nav": [{"id": "overview", "label": "nav.overview", "route": "/overview",
"module": "/static/pages/overview.js", "order": 10, "placement": "main"}],
"modules": ["/static/ext/reports/index.js"],
"capabilities": {"collection": true, "ai_api": false, "ai_copilot": true, "hosted": false, "idle_stop": false},
"project": {"open": true, "id": "coast-3f2a9c1e", "name": "Coastal map"},
"security": {"csrf_header": "X-Cartolex-CSRF", "csrf_cookie": "cartolex_csrf_5b1e0c2a"}
}
Keys¶
key |
what it holds |
|---|---|
|
|
|
|
|
the |
|
the interface languages, the default one, and for each language the catalogues to merge, in order: cartolex’s, then each extension’s |
|
the pages, sorted by |
|
the extensions’ ES modules the shell imports before the first route |
|
what this app can do: |
|
the project the requests work on: |
|
the header state-changing requests carry ( |
Versions¶
Within cartolex-manifest/1, a newer cartolex may add optional keys; an
interface ignores the keys it does not know. A change an interface could
misread (a key removed, a meaning changed) makes the format /2.
tests/fixtures/manifest.example.json is the example interfaces are tested
against, and the app’s tests check that the live manifest holds every key of it.
The schema¶
The schema is generated from cartolex.app.manifest.Manifest and stored with
the package; python -m cartolex.app.schemas --check (run by the tests) fails
when it is out of date, --write regenerates it. It is also served at
GET /api/app/manifest/schema.
{
"$defs": {
"Accent": {
"additionalProperties": false,
"description": "The accent of each theme, ``#rrggbb`` (4.5:1 against the theme's background).",
"properties": {
"dark": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Dark"
},
"light": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Light"
}
},
"title": "Accent",
"type": "object"
},
"AppInfo": {
"additionalProperties": false,
"description": "The application: ``id`` and ``name`` (the host's brand, or cartolex), its version, the\nbuild (``None`` when unknown) and, locally, the system it runs on (for a diagnostic).",
"properties": {
"build": {
"anyOf": [
{
"$ref": "#/$defs/BuildStamp"
},
{
"type": "null"
}
],
"default": null
},
"id": {
"title": "Id",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"platform": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Platform"
},
"version": {
"title": "Version",
"type": "string"
}
},
"required": [
"id",
"name",
"version"
],
"title": "AppInfo",
"type": "object"
},
"BrandingInfo": {
"additionalProperties": false,
"description": "The name and logo the interface shows, and the accent when a host sets one.",
"properties": {
"accent": {
"anyOf": [
{
"$ref": "#/$defs/Accent"
},
{
"type": "null"
}
],
"default": null
},
"logo": {
"title": "Logo",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
},
"required": [
"name",
"logo"
],
"title": "BrandingInfo",
"type": "object"
},
"BuildStamp": {
"additionalProperties": false,
"description": "The code running: its commit and the commit's date (``YYYY-MM-DD``).",
"properties": {
"commit": {
"title": "Commit",
"type": "string"
},
"date": {
"title": "Date",
"type": "string"
}
},
"required": [
"commit",
"date"
],
"title": "BuildStamp",
"type": "object"
},
"Capabilities": {
"additionalProperties": true,
"description": "What this app can do; extensions add flags of their own (extra keys).",
"properties": {
"ai_api": {
"description": "the AI clean-up by API (a key was given)",
"title": "Ai Api",
"type": "boolean"
},
"ai_copilot": {
"description": "the AI clean-up outside the app: the copilot bundle (export, then import)",
"title": "Ai Copilot",
"type": "boolean"
},
"collection": {
"description": "collecting texts from bibliographic services",
"title": "Collection",
"type": "boolean"
},
"hosted": {
"description": "a hosted service (many projects, a host's sign-in)",
"title": "Hosted",
"type": "boolean"
},
"idle_stop": {
"default": false,
"description": "the app stops when no page is open: pages say they are (POST /api/presence)",
"title": "Idle Stop",
"type": "boolean"
}
},
"required": [
"collection",
"ai_api",
"ai_copilot",
"hosted"
],
"title": "Capabilities",
"type": "object"
},
"Locales": {
"additionalProperties": false,
"description": "The interface languages, the default, and each one's catalogues, merged in order.",
"properties": {
"available": {
"items": {
"type": "string"
},
"title": "Available",
"type": "array"
},
"catalogues": {
"additionalProperties": {
"items": {
"type": "string"
},
"type": "array"
},
"title": "Catalogues",
"type": "object"
},
"default": {
"title": "Default",
"type": "string"
}
},
"required": [
"available",
"default",
"catalogues"
],
"title": "Locales",
"type": "object"
},
"NavItem": {
"additionalProperties": false,
"description": "A page: its id, label key, route, module, order and placement.",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"label": {
"title": "Label",
"type": "string"
},
"module": {
"title": "Module",
"type": "string"
},
"order": {
"title": "Order",
"type": "integer"
},
"placement": {
"default": "main",
"enum": [
"main",
"settings",
"hidden"
],
"title": "Placement",
"type": "string"
},
"route": {
"title": "Route",
"type": "string"
}
},
"required": [
"id",
"label",
"route",
"module",
"order"
],
"title": "NavItem",
"type": "object"
},
"ProjectInfo": {
"additionalProperties": false,
"description": "The project the requests work on: open or not, its id and name.",
"properties": {
"id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Id"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Name"
},
"open": {
"title": "Open",
"type": "boolean"
}
},
"required": [
"open"
],
"title": "ProjectInfo",
"type": "object"
},
"Security": {
"additionalProperties": false,
"description": "The header state-changing requests send, and the cookie holding its value.\n\nThe cookie's name carries the app instance's id (two apps on two loopback\nports never share a cookie); cartolex always sends it.",
"properties": {
"csrf_cookie": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Csrf Cookie"
},
"csrf_header": {
"title": "Csrf Header",
"type": "string"
}
},
"required": [
"csrf_header"
],
"title": "Security",
"type": "object"
}
},
"$id": "https://cartolex.github.io/schemas/app/1/manifest.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "``GET /api/app/manifest``: the contract the interface starts from.",
"properties": {
"app": {
"$ref": "#/$defs/AppInfo"
},
"branding": {
"$ref": "#/$defs/BrandingInfo"
},
"capabilities": {
"$ref": "#/$defs/Capabilities"
},
"format": {
"const": "cartolex-manifest/1",
"default": "cartolex-manifest/1",
"title": "Format",
"type": "string"
},
"locales": {
"$ref": "#/$defs/Locales"
},
"modules": {
"items": {
"type": "string"
},
"title": "Modules",
"type": "array"
},
"nav": {
"items": {
"$ref": "#/$defs/NavItem"
},
"title": "Nav",
"type": "array"
},
"project": {
"$ref": "#/$defs/ProjectInfo"
},
"security": {
"$ref": "#/$defs/Security"
}
},
"required": [
"app",
"branding",
"locales",
"nav",
"modules",
"capabilities",
"project",
"security"
],
"title": "Manifest",
"type": "object"
}