tino.routers

tino.routers.buckets

REST endpoints for bucket CRUD operations.

async tino.routers.buckets.create_bucket(body: BucketCreate, user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None), svc: BucketService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Create a new bucket (initializes a git repo with .meta.yml).

async tino.routers.buckets.delete_bucket(slug: str, user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None), svc: BucketService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Delete a bucket and its git repo from disk.

async tino.routers.buckets.get_bucket(slug: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: BucketService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Get metadata for a single bucket.

async tino.routers.buckets.list_buckets(user=Depends(dependency=<function get_current_user>, use_cache=True, scope=None), svc: BucketService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

List buckets the user has access to, with their resolved role.

async tino.routers.buckets.update_bucket(slug: str, body: BucketUpdate, user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None), svc: BucketService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Update a bucket’s description or access rules.

tino.routers.collab

WebSocket endpoint for real-time collaborative editing via Yjs CRDT.

async tino.routers.collab.collab_websocket(websocket: WebSocket, slug: str, path: str)

Yjs WebSocket endpoint for real-time collaborative editing of a file.

tino.routers.compile

REST endpoints for compiling Typst files to SVG or PDF.

async tino.routers.compile.compile_pdf(slug: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: CompilerService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Compile a .typ file and return the PDF.

async tino.routers.compile.compile_svg(slug: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: CompilerService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Compile a .typ file and return a list of SVG strings (one per page).

tino.routers.events

WebSocket endpoint for bucket-level file change notifications.

async tino.routers.events.bucket_events(websocket: WebSocket, slug: str)

Subscribe to file-change events for a bucket.

tino.routers.files

REST endpoints for file CRUD within a bucket’s working tree.

async tino.routers.files.create_dir(slug: str, body: dict, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Create an empty directory in the bucket.

async tino.routers.files.create_file(slug: str, body: FileCreate, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Create a new file in the bucket. Fails if the file already exists.

async tino.routers.files.delete_dir(slug: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), collab: CollabManager = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Delete a directory and all its contents.

async tino.routers.files.delete_file(slug: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), collab: CollabManager = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Delete a file from the bucket’s working tree.

async tino.routers.files.download_zip(slug: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Download all source files in the bucket as a ZIP archive.

async tino.routers.files.list_files(slug: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

List all files and directories in a bucket (excludes .git and .meta.yml).

async tino.routers.files.raw_file(slug: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Serve a file’s raw content (for images, binary previews, etc.).

async tino.routers.files.read_file(slug: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Read a single file’s content by path.

async tino.routers.files.rename_dir(slug: str, body: dict, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), collab: CollabManager = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Rename/move a directory and all its contents.

async tino.routers.files.rename_file(slug: str, body: dict, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), collab: CollabManager = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Rename/move a single file.

async tino.routers.files.save_file(slug: str, path: str, body: FileSave, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Overwrite a file’s content (used by manual save and auto-save).

async tino.routers.files.upload_files(slug: str, files: list[UploadFile] = File(PydanticUndefined), prefix: str = '', user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: FileService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Upload one or more binary/text files via multipart form data. ZIP files are extracted.

tino.routers.fonts

REST endpoints for managing custom fonts (admin-only).

async tino.routers.fonts.delete_font(filename: str, user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None), svc: FontService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Delete a font file.

async tino.routers.fonts.list_fonts(user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None), svc: FontService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Return all installed custom fonts.

async tino.routers.fonts.upload_fonts(files: list[UploadFile] = File(PydanticUndefined), user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None), svc: FontService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Upload one or more font files (TTF, OTF, WOFF, WOFF2).

tino.routers.git

REST endpoints for git operations on a bucket’s repository.

async tino.routers.git.git_changed(slug: str, ref: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

List the files changed in a specific commit.

async tino.routers.git.git_commit(slug: str, body: CommitRequest, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Stage the selected files and create a new commit.

async tino.routers.git.git_diff(slug: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), path: str | None = Query(None), ref: str | None = Query(None))

Return unified diffs.

Without ref the diff covers working-tree changes vs HEAD. With ref it covers the changes introduced by that commit (vs its parent).

async tino.routers.git.git_log(slug: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), path: str | None = Query(None), max_count: int = Query(50))

Return commit history, optionally filtered to a single file path.

async tino.routers.git.git_restore(slug: str, body: RestoreRequest, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None), collab: CollabManager = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Restore file(s) from a specific commit into the working tree.

async tino.routers.git.git_show(slug: str, ref: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Retrieve a file’s content at a specific commit ref.

async tino.routers.git.git_show_raw(slug: str, ref: str, path: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Serve a file’s raw bytes at a specific commit ref (for images).

async tino.routers.git.git_status(slug: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Return per-file working tree status (modified, untracked, deleted, staged).

async tino.routers.git.git_tree(slug: str, ref: str, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: GitService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

List all files at a specific commit ref.

tino.routers.misc

Health, config, theme, and well-known endpoints.

async tino.routers.misc.frontend_config()

Return frontend configuration values.

async tino.routers.misc.health()

Basic health check with version info and bucket count.

async tino.routers.misc.mcp_instructions(user=Depends(dependency=<function require_global_admin>, use_cache=True, scope=None))

Return the full server-level MCP instructions (admin only).

This is the built-in instructions plus the optional TINO_MCP_INSTRUCTIONS override — i.e. everything an agent receives before per-bucket instructions — so the bucket settings dialog can show it read-only alongside the per-bucket field. Admin-gated because it is internal policy text, not public config.

async tino.routers.misc.oauth_authorization_server(request: Request)

RFC 8414 Authorization Server Metadata proxy for MCP CIMD support.

MCP clients such as Claude select CIMD over DCR only when the authorization server metadata advertises both:

  • client_id_metadata_document_supported: true

  • "none" in token_endpoint_auth_methods_supported

Keycloak (tested up to 26.6.3, --features=cimd) satisfies the first condition but never the second. Internally, Keycloak builds the token_endpoint_auth_methods_supported list by iterating its registered ClientAuthenticator provider factories — none of which advertise "none". The presence of public clients in the realm has no effect on the discovery document.

This is a known upstream gap — see keycloak#49730. This proxy is tracked as tino#23 and should be removed once the upstream fix ships.

This endpoint proxies the provider’s metadata and injects the missing value. All other fields — including authorization_endpoint, token_endpoint, and jwks_uri — are forwarded unchanged so the actual OAuth flow goes directly to Keycloak.

The corresponding /.well-known/oauth-protected-resource endpoint lists TINO’s own TINO_BASE_URL as the authorization server so that MCP clients discover this proxy rather than Keycloak’s unpatched metadata.

async tino.routers.misc.oauth_protected_resource()

RFC 9728 Protected Resource Metadata for MCP OAuth discovery.

authorization_servers points to TINO itself (not directly to Keycloak) so that MCP clients fetch the proxied AS metadata from /.well-known/oauth-authorization-server below, which injects "none" into token_endpoint_auth_methods_supported.

scopes_supported limits the scopes MCP clients request. Without it, clients would request every scope from the AS metadata and Keycloak rejects the resulting superset as invalid.

async tino.routers.misc.theme_css()

Return CSS custom properties for the configured accent colour family.

tino.routers.templates

REST endpoints for browsing and initializing Typst templates.

async tino.routers.templates.init_template(slug: str, body: TemplateInit, user=Depends(dependency=<function _require_role.<locals>.dependency>, use_cache=True, scope=None), svc: TemplateService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Initialize a bucket from a Typst template via typst init.

async tino.routers.templates.list_local_templates(user=Depends(dependency=<function get_current_user>, use_cache=True, scope=None), svc: TemplateService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Return templates from the local package directory.

async tino.routers.templates.list_typst_universe_templates(user=Depends(dependency=<function get_current_user>, use_cache=True, scope=None), svc: TemplateService = Depends(dependency=<functools._lru_cache_wrapper object>, use_cache=True, scope=None))

Return all available Typst templates from the Typst Universe package index.