Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

foton is a font package manager for Windows. It works without administrator privileges.

foton can be used in two ways:

  • as a font package manager that installs packages from package registries
  • as a tool for users who want to write package manifests or set up custom package registries

This book covers both workflows.

Who this book is for

This book is for anyone who wants to use foton on Windows. It covers both everyday package management tasks and more advanced workflows such as writing package manifests and setting up custom package registries.

Where to start

Windows-only tool

foton is supported on Windows only.

Installing Foton

foton is supported on Windows only.

Install a pre-built binary

Pre-built binaries are published on the GitHub Releases page.

If you already use cargo-binstall, you can install foton with:

cargo binstall foton

If you prefer, you can also download a release archive manually from the Releases page. When installing this way, make sure foton.exe is placed in a directory on your PATH, or add the extraction directory to your PATH yourself.

Install with Cargo

To install foton with Cargo, first install the Rust toolchain. See the Rust installation guide if you do not have Rust yet.

Then install either the latest released version or the current development version from the GitHub repository.

cargo install foton
cargo install --git https://github.com/gifnksm/foton.git foton

Verify the installation

Run:

foton --help

If the command succeeds, foton is installed and available on your PATH.

Next steps

If you are new to foton, read Core Concepts first. Then continue with Basic Usage to learn the everyday package-management workflow.

Core Concepts

This chapter introduces a few terms that appear throughout the rest of the book. You do not need to memorize them, but knowing them makes the command and guide pages easier to follow.

Packages

In foton, the main unit of installation is a package. A package is a versioned definition of one or more fonts. When you install, update, list, inspect, or uninstall something with foton, you are working with packages.

Package names and versions

Many commands accept either a package name or a package name with an exact version.

  • <package-name>
  • <package-name>@<version>

Use <package-name> when you want to refer to a package by name and let the command decide how to handle matching versions. Use <package-name>@<version> when you want to select an exact version, or when a command asks you to disambiguate.

Package registries

Packages are usually installed from package registries. A package registry is a collection of package definitions that foton can search and install from.

A package registry can be:

  • a local directory on your machine
  • a Git repository that foton fetches and caches locally

The default configuration includes the public foton package registry, which is backed by the gifnksm/foton-registry repository. You can also define your own package registries. See Setting Up Your Own Package Registry for a practical guide. For reference details, see Package Registry Reference and Configuration File Reference.

Manifest files

A manifest file is a TOML file that defines a package. It contains package metadata and one or more downloadable sources from which font files are installed.

Most users will work with packages from package registries. Manifest files become important when you want to:

  • install a package directly from a local manifest file
  • validate a package definition with foton manifest check
  • publish packages through your own package registry

See Writing a Package Manifest for a step-by-step guide and Package Manifest Reference for a field-by-field reference.

Activation state

An installed package can be either active or inactive.

Installed means the package files are stored under foton’s local package storage and the package is recorded in the local package database. Active means that package’s fonts are registered in the Windows registry and available for normal use by applications. Inactive packages remain installed, but their fonts are not registered for use until you activate them.

These states are separate so that you can keep packages installed without keeping all of them active at the same time. For example, you may want to activate only the fonts you currently need for day-to-day use, while leaving other installed packages inactive until needed.

Keeping installation and activation separate also lets you keep multiple installed versions of the same package and choose which version is active.

By default, foton install activates newly installed packages. If you install with --no-activate, you can activate the package later with foton activate and deactivate it again with foton deactivate.

Where to go next

Basic Usage

This section covers the everyday workflow for using foton as a font package manager.

If terms like package, package registry, or manifest file are unfamiliar, read Core Concepts first.

A typical workflow looks like this:

  1. Discover available packages
  2. Install the packages you want
  3. Update installed packages when newer versions are available
  4. Inspect, activate or deactivate, or remove packages that are already installed

Chapters in this section

If you want to install a package directly from a local manifest file or define your own package registry, continue to Advanced Usage.

Discovering Packages

Use foton search to find packages that are available in your registries.

In the examples below, replace placeholders such as <query> and <registry-id> with real values.

Search for a package by name:

foton search <query>

Search with multiple query terms:

foton search <query-word-1> <query-word-2>

For multi-term searches, all query terms must match either the package name or the description.

Restrict the search to specific registries

If you want to search only selected package registries, pass --registry with a comma-separated list of package registry IDs.

foton search --registry <registry-id-1>,<registry-id-2> <query>

Control the number of results

By default, foton search shows up to 10 matching packages. Use --limit to change that number.

foton search --limit 20 <query>

Read the results

Search results are printed as package names with versions together with the registry ID that provided the package. If a package has a description, it is shown on the next line.

Example output:

example-font@1.2.3 [example]
  Example font family for UI and coding

By default, search does not include pre-release versions. Use --pre-release if you want search results to include pre-release versions. Once you have found a package you want, install it with foton install.

Installing and Updating Packages

This chapter covers the commands you will use most often after discovering a package: install and update.

In the examples below, replace placeholders such as <package-name>, <version>, and <registry-id> with real values.

Install packages from package registries

Install a package by name:

foton install <package-name>

Install multiple packages at once:

foton install <package-name-1> <package-name-2>

Install a specific version:

foton install <package-name>@<version>

By default, foton resolves packages from the package registries enabled in your configuration. To resolve packages only from specific package registries, pass --registry.

foton install --registry <registry-id-1>,<registry-id-2> <package-name>

Install without activating immediately

By default, install activates the selected packages after they are installed. If you only want to download and record the package first, pass --no-activate.

foton install --no-activate <package-name>

You can activate the package later with foton activate.

Update installed packages

Update installed packages when newer versions are available:

foton update

Update only selected packages:

foton update <package-name-1> <package-name-2>

Updating a package installs the newer version without automatically removing older installed versions.

If you want to control which package registries are used to find updates, pass --registry.

foton update --registry <registry-id-1>,<registry-id-2> <package-name>

Confirmation prompts

Commands that change installed packages ask for confirmation before applying changes. If you want to skip the prompt, pass the global --no-confirm option.

foton --no-confirm install <package-name>
foton --no-confirm update

Notes

  • If an install or update does not complete cleanly, use foton repair to clean up any packages left behind.
  • See the command reference for additional options and details.

Managing Installed Packages

This chapter covers the commands you use after packages have already been installed: list, info, activate, deactivate, and uninstall. It also explains how to recover from incomplete operations with repair.

In the examples below, replace placeholders such as <package-name> and <version> with real values.

List packages

Show packages recorded in the local package database:

foton list

list shows packages recorded in the local package database.

If you see packages in incomplete states, inspect them with foton info, then clean them up with foton repair. Most of the time, you will work only with installed packages.

If you want machine-readable output, use JSON Lines:

foton list --format jsonl

Inspect a package in detail

Show detailed information about one or more packages recorded in the local package database:

foton info <package-name>
foton info <package-name>@<version>

info prints the package ID, recorded states, and package metadata for matching packages recorded in the local package database. For packages in the installed state, it also shows a summary of the installed font families. Use --include-files when you also want the fonts directory and installed font files for packages in the installed state. This can include packages left by incomplete operations. Use this command when you want to confirm exactly what is recorded in the local package database.

Change whether an installed package is active

Installed packages can stay available in foton’s local package storage while being either active or inactive. Active packages have their fonts registered in the Windows registry and available for normal use by applications. Inactive packages remain installed, but their fonts are not registered for use until you activate them.

This is most useful when you installed a package with --no-activate, when you want to keep only the fonts you currently need active for day-to-day use, or when you want to switch versions manually.

Activate one or more installed packages:

foton activate <package-name>
foton activate <package-name>@<version>

Only one version of a package name can be active at a time. When you activate one version, foton registers that version’s fonts for use and deactivates any other active version of that package name automatically. Use an exact version when multiple versions of the same package are installed, or when you want to target a specific installed version.

Deactivate one or more installed packages:

foton deactivate <package-name>
foton deactivate <package-name>@<version>

Use an exact version when multiple versions of the same package are installed, or when you want to target a specific installed version.

Like other commands that change installed packages, activate and deactivate ask for confirmation before applying changes. Use the global --no-confirm option if you want to skip the prompt.

Recover from operations that did not complete cleanly

Sometimes a package command such as install or activate does not complete cleanly. It may leave packages in incomplete states. If list shows such packages, use repair to clean them up:

foton repair

You can also target a specific package:

foton repair <package-name>
foton repair <package-name>@<version>

repair cleans up those packages. It does not retry or resume the command that left them in those states.

Remove a package

Uninstall one or more packages:

foton uninstall <package-name>
foton uninstall <package-name-1> <package-name-2>
foton uninstall <package-name>@<version>

Use an exact version when multiple versions of the same package are installed, or when you want to target a specific installed version.

Like install and update, uninstall asks for confirmation before applying changes. If an uninstall does not complete cleanly, use foton repair to clean up any packages it leaves behind. If you want to skip the prompt, pass the global --no-confirm option.

foton --no-confirm uninstall <package-name>

Typical workflow

A common workflow is:

  1. Run foton list to see what is installed
  2. Run foton info <package-name> to inspect a package in detail
  3. Run foton activate <package-name> or foton deactivate <package-name> when you want to change whether an installed package is active
  4. Run foton uninstall <package-name> to remove a package you no longer need

Advanced Usage

This section is for users who want to do more than install packages from the public package registry.

Typical advanced workflows include:

  • writing your own package manifest
  • validating that manifest before publishing or installing it
  • testing a manifest locally with foton install --manifest
  • setting up a custom package registry
  • using local and Git-backed package registries from your configuration

Suggested workflow

If you are creating a package, a typical workflow looks like this:

  1. Write a package manifest
  2. Run foton manifest check to validate it
  3. Test it locally with foton install --manifest
  4. Add it to a package registry if you want to distribute it

Chapters in this section

Writing a Package Manifest

A package manifest is a TOML file that defines a font package. It tells foton what the package is called, where its downloadable sources are, and which fonts from those sources should be installed.

Typical workflow

A practical workflow for authoring a package is:

  1. Write a manifest file
  2. Run foton manifest check on it
  3. Install it locally with foton install --manifest
  4. Add it to a package registry if you want to publish it

Example manifest

name = "example-font"
version = "1.2.3"
description = "Example font family for UI and coding"
homepage = "https://example.com/example-font"
repository = "https://github.com/example/example-font"
license = "OFL-1.1"

[[sources]]
url = "https://example.com/downloads/example-font-1.2.3.zip"
hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

[sources.contents]
type = "archive"
fonts = [
  "example-font-1.2.3/ExampleFont-Regular.ttf",
  "example-font-1.2.3/ExampleFont-Bold.ttf",
  "example-font-1.2.3/ExampleFontUI-Regular.ttf",
  "example-font-1.2.3/ExampleFontUI-Bold.ttf",
]

Required fields

This section is a quick checklist, not a complete field reference. See Package Manifest Reference for a detailed description of every field.

At minimum, a manifest must define:

  • name
  • version
  • sources

For the exact fields inside each source entry, see Package Manifest Reference.

Choosing a package version

Choose a version that identifies one specific immutable release and sorts in release order for that package.

A practical way to choose package versions is:

  • Use the upstream release version when it already fits foton’s package-version format.
  • If there is no usable upstream version, use a calendar-based version such as 2024.05.11.
  • If you need to publish a pre-release, add a suffix to the final part. A suffix marks the version as a pre-release and sorts it before the corresponding version without a suffix, such as 1.4.0-rc-1 < 1.4.0 or 2024.05.11-beta-2 < 2024.05.11.
  • If the upstream version does not fit foton’s package-version syntax, rewrite it in a form that still preserves the upstream release ordering and whether the release is stable or pre-release. For example, rewrite v1.4.0 as 1.4.0, and rewrite 1.4.0-rc10 as 1.4.0-rc-10.
  • Keep the notation consistent within the same package to avoid non-intuitive ordering results:
    • Do not mix forms such as 2024.5.11 and 2024.05.11, because they are different versions.
    • Keep the same number of numeric parts within a package. For example, prefer a consistent series such as 1.2.0, 1.3.0, and 1.4.0-rc-1 over mixing forms such as 1.2, 1.3.0, and 1.4-rc-1.

For the exact package-version syntax and ordering rules, see Package Manifest Reference.

These fields are optional, but strongly recommended because they help users understand the package and the fonts it provides. In particular, description appears in search results and is used by foton search. See Package Manifest Reference for the complete field definitions and constraints.

These fields are recommended:

  • description
  • license
  • homepage
  • repository

These metadata fields describe the package and the fonts it provides, not the manifest structure itself. In particular, homepage, repository, and license should refer to the upstream font project or the upstream font files included in the package.

If there is no suitable upstream homepage or repository, omit that field. Do not repeat repository in homepage just to fill both fields.

foton manifest check warns if description or license is missing.

Prepare source information

Each sources[] entry needs a source digest. For archive sources, you also need the exact archive entry paths for the font files you want to install.

Get the source hash

The hash field stores the SHA-256 digest of the downloaded source file, written as sha256:<hex-digest>. Download the exact file referenced by url, compute its SHA-256 digest with a local tool, and copy the hexadecimal digest into the manifest.

On Windows, one way to do this is:

Get-FileHash -Algorithm SHA256 <downloaded-file-path>

Then write the result in the manifest as:

hash = "sha256:<hex-digest>"

Find archive entry paths

For contents.type = "archive", the fonts and ignore rules use paths inside the ZIP archive, not paths from an extracted directory on disk. Open the ZIP archive in Explorer or another archive tool and copy each entry path exactly as it appears in the archive.

For example, if the archive contains example-font-1.2.3/ExampleFont-Regular.ttf, use that full archive path in fonts.

Choosing fonts from a source

Each sources[] entry must define a [sources.contents] table. That table tells foton what kind of downloaded source it is working with and, for archive sources, which font files inside that source should be installed.

If contents.type = "font-file", the downloaded source itself is one font file. If contents.type = "archive", the downloaded source is a ZIP archive that contains one or more font files. See Package Manifest Reference for the exact field definitions and constraints.

Archive sources (contents.type = "archive")

For an archive source, the fonts field under [sources.contents] selects which archive entries should be installed as fonts. If you omit fonts, foton uses these default glob rules:

  • **/*.ttf
  • **/*.otf
  • **/*.ttc
  • **/*.otc

Prefer fonts entries that list each font file path explicitly. If the archive entry’s file name is unsuitable, you can also specify file-name on a path entry written as { path = ... } to tell foton what file name to store locally. Avoid glob rules when possible. This makes it clear from the manifest exactly which files belong to the package, and it reduces the chance of unintentionally picking up extra or unexpected files from the source archive.

If the source archive contains other font-like files such as *.ttf, *.otf, *.ttc, or *.otc that you do not want to install, prefer listing those paths in ignore explicitly. That makes the omission visible in the manifest and shows that the files were left out intentionally.

Direct font sources (contents.type = "font-file")

For a source with contents.type = "font-file", the downloaded source itself is installed as one font file. You can set file-name to override the local file name when the URL path is not suitable.

[[sources]]
url = "https://example.com/download?id=123"
hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

[sources.contents]
type = "font-file"
file-name = "Example-Regular.ttf"

Validate the manifest

Run:

foton manifest check <manifest-path>

By default, manifest check does more than syntax validation. It reads the manifest, stages the package, downloads the sources, and verifies that installation would succeed. Use --no-source-checks to skip those source-dependent checks when needed.

For common warning types, see Package Manifest Reference.

If you want warnings to fail the command, use the global --warnings-as-errors option.

foton --warnings-as-errors manifest check <manifest-path>

Test the manifest locally

You can install a package directly from a local manifest file:

foton install --manifest <manifest-path>

This is useful before publishing the manifest in a registry. It lets you test the actual install workflow with the same manifest content.

Publish through a registry

Once a manifest works locally, place it in a package registry so it can be resolved by package name. See Setting Up Your Own Package Registry.

Setting Up Your Own Package Registry

A package registry is a collection of package manifests that foton can search and install from. You can use your own package registry to distribute internal packages, test packages before publishing them elsewhere, or maintain a curated set of fonts.

This chapter focuses on the setup workflow. For the exact registry layout and configuration format, see Package Registry Reference and Configuration File Reference.

Registry types

A custom package registry can be managed in two common ways:

  • as a local directory on your machine
  • as a Git repository that stores the registry root and its manifests

A local directory is convenient for local testing or personal use. A Git-backed registry is useful when you want version history, code review, or sharing across multiple machines or users.

The default configuration already includes the public foton package registry. Custom registries are added through your config.toml file.

Registry layout

A registry stores package manifests in this directory layout:

<registry-root>/
  .foton-registry-root
  packages/
    <package-name>/
      <version>/
        manifest.toml

Example:

my-registry/
  .foton-registry-root
  packages/
    example-font/
      1.2.3/
        manifest.toml
    another-font/
      2.0.0/
        manifest.toml

Each package version has its own manifest.toml file. This layout lets a single registry contain multiple versions of the same package. The .foton-registry-root marker file is recommended. It is not required for package discovery, but it makes the registry root explicit and helps foton manifest check detect the registry root automatically.

Setup workflow

A practical workflow is:

  1. Create the registry root directory and add .foton-registry-root
  2. Place each manifest at packages/<package-name>/<version>/manifest.toml
  3. Validate the manifests in place
  4. Add the registry to your config.toml file
  5. Use that registry from foton

The sections below cover each part of that workflow.

Create the registry root

Create the registry root directory and add the .foton-registry-root marker file. This makes the registry root explicit and lets foton manifest check detect it automatically when validating manifests by file path.

If you want to manage the registry through Git, make the registry root a Git repository, for example by running git init there or by placing it inside an existing repository.

Add package manifests

Place each manifest at packages/<package-name>/<version>/manifest.toml in the registry. If you need help writing a manifest, see Writing a Package Manifest.

Validate package manifests

When the manifest is already stored under a registry root that contains .foton-registry-root, manifest check can detect that registry root automatically. In the normal case, you can validate a manifest in place with:

foton manifest check <manifest-path>

When validating many manifests in a registry at once, it can be useful to skip source-dependent checks first:

foton manifest check --no-source-checks <registry-root>\packages\**\manifest.toml

Add the registry to your config

Once the manifests are in place, add the registry to your config.toml file. You can configure it either as a local directory or as a Git-backed registry.

Local registry source

Use local+<absolute-path> for a registry stored in a local directory. The path must be absolute.

[registries.example]
source = "local+C:/path/to/my-registry"
enabled = true

Git registry source

Use git+<url> for a registry stored in a Git repository.

[registries.example]
source = "git+https://example.com/fonts/example-registry.git"
enabled = true

foton caches Git registries locally and updates the cached repository when it fetches registry contents.

Enable or disable a registry

Each registry entry has an enabled flag. If omitted, it defaults to true. Set it to false when you want to keep the registry definition in your config.toml file without using it by default.

[registries.example]
source = "local+C:/path/to/experimental-registry"
enabled = false

Use the registry from foton

Search or install from that package registry by ID:

foton search --registry example <query>
foton install --registry example <package-name>

You can also opt in to a disabled package registry explicitly with --registry <registry-id>. If multiple package registries contain the same package name, use --registry to choose which registry to use.

Package Manifest Reference

A package manifest is a TOML document that defines a single package version. It is used both for packages stored in registries and for local manifest files installed with foton install --manifest.

Format overview

A manifest uses kebab-case field names. Unknown fields are rejected.

At the top level, a manifest contains package metadata and a non-empty sources array.

Example

name = "example-font"
version = "1.2.3"
description = "Example font family for UI and coding"
homepage = "https://example.com/example-font"
repository = "https://github.com/example/example-font"
license = "OFL-1.1"

[[sources]]
url = "https://example.com/downloads/example-font-1.2.3.zip"
hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

[sources.contents]
type = "archive"
fonts = [
  "example-font-1.2.3/ExampleFont-Regular.ttf",
  "example-font-1.2.3/ExampleFont-Bold.ttf",
]

Validation and quality checks

Use foton manifest check to validate a manifest. The command checks both installation errors and quality issues. For example, it can report:

  • missing recommended metadata such as description or license
  • metadata issues such as an empty or whitespace-padded description, an invalid homepage or repository, identical homepage and repository values, or an invalid license
  • for manifests treated as part of a package registry, a path that does not match the registry path for the manifest’s package ID
  • source-content issues such as:
    • for sources with contents.type = "archive":
      • glob entries in fonts
      • fonts or ignore rules that match nothing
      • font-like files that match neither fonts nor ignore

Package version format and ordering

In a package manifest, the version field uses foton’s package-version format. It identifies a single immutable package release and is also used to order package versions when foton selects newer or older releases.

Format

A package version follows this grammar:

<package-version> = <numeric-part> ("." <numeric-part>)* [<suffix>]
<suffix> = "-" <alpha-identifier> ("-" <suffix-identifier>)*
<numeric-part> = <digit>+
<alpha-identifier> = <lowercase-letter>+
<suffix-identifier> = <alpha-identifier> | <digit>+
<digit> = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
<lowercase-letter> = "a" | ... | "z"

Additional rules:

  • <suffix> may appear only after the final <numeric-part>
  • <suffix> denotes a pre-release of the same <package-version> without the <suffix>
  • <suffix-identifier> values are written as separate --delimited tokens, so use 1.0.0-rc-10, not 1.0.0-rc10

Valid examples:

  • 2407.24
  • 2024.05.11
  • 1.2.3
  • 1.0.0-rc-1

Invalid examples:

  • 1-rc.10
  • 1.0-beta.2
  • 1-rc10
  • 1-RC

Comparison rules

Versions are compared in this order:

  1. Compare corresponding <numeric-part> values from left to right.
  2. Compare each <numeric-part> by numeric value.
  3. If two <numeric-part> values have the same numeric value, the one with fewer leading zeros is older.
  4. If all shared <numeric-part> values are equal and one <package-version> has fewer <numeric-part> values, the shorter version is older.
  5. A <package-version> with a <suffix> is a pre-release and is older than the same <package-version> without a <suffix>.
  6. If both versions have <suffix> values, compare corresponding <suffix-identifier> values from left to right.
  7. Numeric <suffix-identifier> values are compared by numeric value and are older than alphabetic <suffix-identifier> values.
  8. If two numeric <suffix-identifier> values have the same numeric value, the one with fewer leading zeros is older.
  9. If all shared <suffix-identifier> values are equal and one <suffix> has fewer identifiers, the shorter suffix is older.

Comparison examples

  • Numeric parts are compared from left to right:
    • 1.2.0 < 1.10.0
    • 1.2 < 1.2.0
    • 1 < 1.0-rc < 1.0
  • Leading zeros affect ordering when the numeric values are otherwise equal:
    • 5 < 05 < 005
    • 2024.5.11 < 2024.05.11
  • A suffix makes a version older than the same version without a suffix:
    • 1.0.0-rc < 1.0.0
    • 2024.05.11-beta-2 < 2024.05.11
  • Suffix identifiers are compared from left to right:
    • 1.0.0-alpha < 1.0.0-beta
    • 1.0.0-rc-2 < 1.0.0-rc-10
    • 1.0.0-rc-1 < 1.0.0-rc-a
    • 1.0.0-rc < 1.0.0-rc-1

Top-level fields

Field headings indicate whether a field is required or optional. Fields marked recommended are optional, but strongly recommended because they help users understand the package and the fonts it provides. In particular, description appears in search results and is used by foton search. Metadata fields such as description, homepage, repository, and license describe the package and the fonts it provides. Packaging fields such as name, version, and sources describe how foton identifies and installs the package.

name (required)

The canonical package name used in commands such as foton install <package-name>. This is the stable identifier for the package.

  • Type: package name string

  • Constraints: must start with a lowercase ASCII letter and contain only lowercase ASCII letters, digits, or -

  • Example:

    name = "example-font"
    

version (required)

The package version. Use a version string that identifies an immutable package release.

  • Type: package version string

  • Constraints: must follow the package-version format described above

  • Example:

    version = "1.2.3"
    

A short description of the font family, collection, or bundle provided by the package. Use this field for a concise, user-facing summary of what the package provides. Keep this field non-empty and free of leading or trailing whitespace.

  • Type: string

  • Recommended because: it appears in search results and can help users find the package

  • Example:

    description = "Example font family for UI and coding"
    

The upstream homepage for the font project or distribution represented by the package. When setting this field, use a valid http or https URL. Omit this field if there is no distinct upstream homepage.

  • Type: URL string

  • Recommended because: it gives users a homepage for more information about the fonts provided by the package

  • Note: if the upstream homepage and source repository would be the same URL, prefer repository and omit homepage

  • Example:

    homepage = "https://example.com/example-font"
    

The upstream source repository for the font project represented by the package. When setting this field, use a valid http or https URL. Omit this field if there is no suitable public upstream repository.

  • Type: URL string

  • Recommended because: it gives users a source repository for the upstream font project

  • Example:

    repository = "https://github.com/example/example-font"
    

The SPDX license expression for the upstream font files included in the package. When setting this field, use a valid SPDX expression. Use the license of the upstream font files included in the package, not the license of the manifest itself.

  • Type: SPDX expression string

  • Recommended because: it tells users the licensing terms of the font files included in the package

  • Example:

    license = "OFL-1.1"
    

sources (required)

A non-empty array of source objects. Each source describes one downloadable font file or archive from which foton can install fonts.

  • Type: non-empty array of source objects

  • Example:

    [[sources]]
    url = "https://example.com/downloads/example-font-1.2.3.zip"
    hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    
    [sources.contents]
    type = "archive"
    

Source fields

Field headings indicate whether a field is required or optional. Each entry in sources supports the following fields.

sources[].url (required)

The downloadable archive or file that contains the package contents.

  • Type: URL string

  • Constraints: the URL scheme must be http or https

  • Example:

    [[sources]]
    url = "https://example.com/downloads/example-font-1.2.3.zip"
    

sources[].hash (required)

The expected digest used to verify source integrity.

  • Type: digest string

  • Constraints: must include an algorithm prefix such as sha256:; currently sha256 is supported

  • Example:

    [[sources]]
    hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    

sources[].contents (required)

A table that describes what the downloaded source contains. Use contents.type = "font-file" when the downloaded source is itself one font file. Use contents.type = "archive" when the downloaded source is a ZIP archive that contains one or more font files.

  • Type: contents table

  • Example:

    [[sources]]
    url = "https://example.com/downloads/example-font-1.2.3.zip"
    hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    
    [sources.contents]
    type = "archive"
    

sources[].contents.type (required)

The kind of downloaded source.

  • Type: string

  • Allowed values:

    • "font-file": the downloaded source is one font file
    • "archive": the downloaded source is a ZIP archive that contains one or more font files
  • Note: support for ZIP compression methods and related ZIP features depends on foton’s current ZIP reader implementation. See the zip crate documentation for the currently supported methods and extensions: https://docs.rs/zip/8.6.0/zip/

  • Example:

    [sources.contents]
    type = "font-file"
    

sources[].contents.file-name (optional, for sources with contents.type = "font-file")

The file name that foton should use for a source with contents.type = "font-file". When omitted, foton derives the file name from the URL path.

  • Type: file name string

  • Constraints: must be a valid plain file name

  • Example:

    [[sources]]
    url = "https://example.com/download?id=123"
    hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    
    [sources.contents]
    type = "font-file"
    file-name = "Example-Regular.ttf"
    

Rules that select font files from a source with contents.type = "archive". Each entry is either:

  • a path entry, written either as a string or as { path = ... }
  • a glob entry, written as { glob = ... }

A string path entry is shorthand for { path = "..." }. Only path entries written as { path = ... } may also specify file-name. glob entries must not specify file-name. If omitted, foton uses the default font-file glob rules.

  • Type: non-empty array of font rules

  • Constraints:

    • each path entry must specify a non-empty path
    • each glob entry must specify a valid, non-empty glob
    • file-name may be specified only on path entries written as { path = ... }
    • when present, file-name must be a valid plain file name
  • Default: { glob = "**/*.ttf" }, { glob = "**/*.otf" }, { glob = "**/*.ttc" }, and { glob = "**/*.otc" }

  • Recommended because: it makes the package contents explicit and reduces unintended matches from the source archive

  • Notes:

    • if a path entry written as { path = ... } specifies file-name, foton stores the extracted font file using that file name instead of the archive entry’s original file name
    • if a path entry written as { path = ... } does not specify file-name, foton keeps the archive entry’s original file name
    • string path entries and glob entries keep each archive entry’s original file name
  • Examples:

    [[sources]]
    url = "https://example.com/downloads/example-font-1.2.3.zip"
    hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    
    [sources.contents]
    type = "archive"
    fonts = [
      "fonts/ExampleFont-Regular.ttf",
      { path = "fonts/ExampleFont-Bold.ttf", file-name = "Example-Bold.ttf" },
      { glob = "fonts/ExampleFontUI-*.ttf" },
    ]
    

sources[].contents.ignore (optional, for sources with contents.type = "archive")

Rules that exclude archive entries from a source with contents.type = "archive" even if they match fonts. Each entry may be written as:

  • a path entry, written either as a string or as { path = ... }
  • a glob entry, written as { glob = ... }

If a path matches both fonts and ignore, ignore takes precedence.

  • Type: array of ignore rules

  • Example:

    [[sources]]
    url = "https://example.com/downloads/example-font-1.2.3.zip"
    hash = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    
    [sources.contents]
    type = "archive"
    ignore = [
      "fonts/Extra.ttf",
      { glob = "fonts/legacy/*.ttf" },
    ]
    

Package Registry Reference

A package registry is a collection of package manifests that foton can search and install from. A registry may be stored in a local directory or in a Git repository.

Directory layout

A registry is organized by package name and package version:

<registry-root>/
  .foton-registry-root
  packages/
    <package-name>/
      <version>/
        manifest.toml

Example:

registry/
  .foton-registry-root
  packages/
    example-font/
      1.2.3/
        manifest.toml
    another-font/
      2.0.0/
        manifest.toml

The package ID in the manifest must match the directory that contains it. In other words, the manifest at packages/example-font/1.2.3/manifest.toml must describe example-font@1.2.3.

The .foton-registry-root file is recommended and marks the directory as a registry root explicitly. It also helps foton manifest check detect the registry root automatically when validating manifests by file path.

When foton scans the registry layout:

  • under packages, only directories are considered package-name entries; hidden entries and other non-directory entries are ignored
  • under each <package-name> directory, only directories are considered version entries; hidden entries and other non-directory entries are ignored
  • within each <version> directory, foton reads manifest.toml; other files are ignored for package discovery

Registry configuration

Registries are configured in your config.toml file under [registries.<registry-id>]. <registry-id> is a user-defined identifier such as foton, local, or team. Commands that support --registry use these registry IDs, not paths or URLs.

Each registry entry specifies a source. foton currently supports these source formats:

  • local+<absolute-path>
  • git+<url>

A local+... source points directly at a directory on your machine. A git+... source points at a Git repository URL and is fetched into a local cache before use.

Example:

[registries.example]
source = "local+C:/path/to/my-registry"
enabled = true
foton search --registry example <query>

For the exact configuration format, see Configuration File Reference.

Configuration File Reference

foton loads configuration from a config.toml file in its configuration directory. If the file does not exist, built-in defaults are used.

On Windows, the configuration file is stored at:

%APPDATA%\io.github.gifnksm\foton\config\config.toml

What configuration controls

The configuration file currently controls:

  • install-time safety limits for downloaded package sources and the font files selected from them
  • the set of configured package registries
  • whether each configured registry is enabled by default

Default behavior and merge rules

config.toml is optional. If it is present, foton merges it onto built-in defaults.

This means:

  • omitted sections and keys keep their default values
  • unknown keys are rejected
  • the default configuration already includes the public foton package registry

Example configuration

[install]
max-source-size-bytes = 536870912
max-fonts-per-package-source = 1000
max-font-file-size-bytes = 134217728

[registries.foton]
source = "git+https://github.com/gifnksm/foton-registry.git"
enabled = true

[registries.local]
source = "local+C:/path/to/my-registry"
enabled = true

[registries.experimental]
source = "git+https://example.com/fonts/experimental-registry.git"
enabled = false

Top-level sections

Field headings indicate whether a section or key is optional. Defaults are listed in the metadata bullets for the relevant sections and keys.

install (optional)

The install section defines safety limits used while processing package sources. All install limit values must be positive integers greater than zero.

  • Type: table

  • Default: built-in install limits are used if this section is omitted

  • Example:

    [install]
    max-source-size-bytes = 536870912
    max-fonts-per-package-source = 1000
    max-font-file-size-bytes = 134217728
    

registries (optional)

The registries table contains registries.<registry-id> entries keyed by registry ID. Each registries.<registry-id> entry configures one registry that foton can search or install from.

  • Type: table of registry entries

  • Default: the built-in registries.foton entry is present even if this section is omitted

  • Example:

    [registries.example]
    source = "local+C:/path/to/my-registry"
    enabled = true
    

Install section fields

install.max-source-size-bytes (optional)

The maximum allowed size, in bytes, of one downloaded package source. This limit applies before foton interprets the source as a direct font file or as an archive.

  • Type: positive unsigned integer

  • Default: 536870912 (512MiB)

  • Example:

    [install]
    max-source-size-bytes = 536870912
    

install.max-fonts-per-package-source (optional)

The maximum number of font files that foton may take from one package source. For sources with contents.type = "archive", this limits how many archive entries may become installed font files. For sources with contents.type = "font-file", the source itself counts as one font file.

  • Type: positive unsigned integer

  • Default: 1000

  • Example:

    [install]
    max-fonts-per-package-source = 1000
    

install.max-font-file-size-bytes (optional)

The maximum allowed size, in bytes, of one font file selected for installation. This limit applies both to sources with contents.type = "font-file" and to individual font files selected from sources with contents.type = "archive".

  • Type: positive unsigned integer

  • Default: 134217728 (128MiB)

  • Example:

    [install]
    max-font-file-size-bytes = 134217728
    

Registry entry fields

registries.<registry-id> (optional)

A single registry entry. <registry-id> is a user-defined registry ID. Commands such as foton search --registry ... and foton install --registry ... use these registry IDs.

  • Type: table

  • Constraints: the registry ID must start with a lowercase ASCII letter and contain only lowercase ASCII letters, digits, or -

  • Example:

    [registries.example]
    source = "local+C:/path/to/my-registry"
    enabled = true
    

registries.<registry-id>.source (required)

The source from which foton loads the registry. Use local+... for a local directory or git+... for a Git-backed registry.

  • Type: registry source string

  • Constraints: must be either local+<absolute-path> or git+<url>

  • Example:

    [registries.example]
    source = "local+C:/path/to/my-registry"
    
    [registries.example]
    source = "git+https://example.com/fonts/example-registry.git"
    

registries.<registry-id>.enabled (optional)

Whether the registry is enabled by default. Set this to false when you want to keep the registry configured but not use it unless you opt in explicitly.

If you pass --registry <REGISTRY_ID>, foton can still use that package registry even when enabled = false. If all configured package registries are disabled and you do not pass --registry, commands that rely on package registries fail because there are no enabled package registries to use.

  • Type: boolean

  • Default: true

  • Example:

    [registries.example]
    enabled = false
    

Command Reference

This section documents the foton command-line interface.

Command structure

The general command format is:

foton [GLOBAL_OPTIONS] <COMMAND> [COMMAND_OPTIONS] [ARGS...]

Run foton --help to see the complete command-line help.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Commands

  • install: Install packages from package registries or manifest files
  • update: Update installed packages from package registries
  • uninstall: Uninstall packages recorded in the local package database
  • activate: Activate installed packages
  • deactivate: Deactivate installed packages
  • repair: Clean up packages left in incomplete states
  • list: List packages recorded in the local package database
  • info: Show detailed information about packages recorded in the local package database
  • search: Search packages in package registries
  • manifest: Work with package manifest files
  • font: Work with fonts managed by foton

install

Install packages from package registries or manifest files.

Usage

foton install [OPTIONS] [<PACKAGE>...] [--manifest <MANIFEST>...]

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

At least one <PACKAGE> or --manifest is required.

Options

--manifest <MANIFEST>

Install packages defined in the given manifest files.

This option can be specified multiple times.

--registry <REGISTRY_ID>

Package registry IDs to resolve packages from.

Use a comma-separated list such as --registry local,foton. This option applies only to packages installed by <PACKAGE>.

--pre-release

Allow installing pre-release versions when resolving packages from registries.

Without this option, versions with a suffix such as 1.2.3-rc-1 are ignored unless an exact version is specified. This option applies only to packages installed by <PACKAGE>.

--no-activate

Do not activate the installed packages. Use foton activate later if you want to make them active manually.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton install <package-name>
foton install <package-name>@<version>
foton install --registry <registry-id-1>,<registry-id-2> <package-name>
foton install --manifest <manifest-path>

Notes

  • Use foton update when you want to look for newer versions of already installed packages in package registries.
  • If an install does not complete cleanly, use repair to clean up any packages it leaves behind.
  • Installing from a manifest file is useful for local testing before adding it to a package registry.

update

Update installed packages from package registries.

Usage

foton update [OPTIONS] [<PACKAGE>...]

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

If not specified, update checks installed packages for newer versions.

Options

--registry <REGISTRY_ID>

Package registry IDs to resolve packages from.

Use a comma-separated list such as --registry local,foton.

--pre-release

Allow updating to pre-release versions when resolving packages from registries.

Without this option, versions with a suffix such as 1.2.3-rc-1 are ignored.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton update
foton update <package-name>
foton update --registry <registry-id-1>,<registry-id-2> <package-name>

Notes

  • If an update does not complete cleanly, use repair to clean up any packages it leaves behind.
  • Updating a package installs the newer version without automatically removing older installed versions.
  • Update resolution uses package names and package registry IDs, not manifest files.

uninstall

Uninstall packages recorded in the local package database.

Usage

foton uninstall [OPTIONS] <PACKAGE>...

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton uninstall <package-name>
foton uninstall <package-name-1> <package-name-2>
foton uninstall <package-name>@<version>

Notes

  • If an uninstall does not complete cleanly, use repair to clean up any packages it leaves behind.
  • uninstall operates on packages recorded in the local package database and does not access package registries.

activate

Activate installed packages.

Usage

foton activate [OPTIONS] <PACKAGE>...

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton activate <package-name>
foton activate <package-name>@<version>
foton activate <package-name-1> <package-name-2>

Notes

  • activate operates on packages recorded in the local package database and does not access package registries.
  • Only one version of a package name can be active at a time. Activating one version deactivates any other active version of the same package name.

deactivate

Deactivate installed packages.

Usage

foton deactivate [OPTIONS] <PACKAGE>...

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton deactivate <package-name>
foton deactivate <package-name>@<version>
foton deactivate <package-name-1> <package-name-2>

Notes

  • deactivate operates on packages recorded in the local package database and does not access package registries.
  • Use repair when a package command such as install or activate does not complete cleanly and leaves packages in incomplete states.

repair

Clean up packages left in incomplete states when commands such as install or activate do not complete cleanly.

repair only performs cleanup. It does not retry or resume those commands.

Usage

foton repair [OPTIONS] [<PACKAGE>...]

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

If not specified, every package that needs cleanup will be cleaned up.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton repair
foton repair <package-name>
foton repair <package-name>@<version>

Notes

  • If cleanup cannot be completed, the package may remain in the local package database so that you can retry repair later.

list

List packages recorded in the local package database.

Usage

foton list [OPTIONS] [<PACKAGE>...]

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

If not specified, show all packages in the local package database.

Options

--format <FORMAT>

Select the output format.

  • Default: text
  • Possible values: text, jsonl

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

Show packages recorded in the local package database:

foton list

Show packages as JSON Lines:

foton list --format jsonl

Output

list prints packages recorded in the local package database. In text output, each line contains a package name, version, installation state, and activation state:

example-font@1.2.3 (installed, active)

With --format jsonl, list writes one JSON object per listed package in JSON Lines format.

Notes

  • list reads the local package database and does not access package registries.
  • Use info when you want more than the package name, version, and recorded states.

info

Show detailed information about packages recorded in the local package database.

Usage

foton info [OPTIONS] <PACKAGE>...

Arguments

<PACKAGE>

Package names, optionally with an exact version as <package-name>@<version>.

Options

--include-files

For installed packages, also include the fonts directory and installed font files.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton info <package-name>
foton info <package-name>@<version>
foton info --include-files <package-name>

Output

info prints detailed information for matching packages recorded in the local package database, including:

  • package ID
  • installation state and activation state
  • description, homepage, repository, and license
  • for packages in the installed state, a summary of the installed font families

With --include-files, info also shows the fonts directory and the installed font files for packages in the installed state.

Notes

  • info reads the local package database and does not search package registries.
  • Use activate or deactivate when you want to change a package’s activation state.
  • Use repair when you want to clean up packages left by incomplete operations.
  • Use search when you want to inspect packages that are available in a package registry but not yet installed.

search

Search packages in package registries.

Usage

foton search [OPTIONS] <QUERY>...

Arguments

<QUERY>

Search query terms.

All specified query terms must match within the same package metadata field.

Options

--registry <REGISTRY_ID>

Package registry IDs to search.

Use a comma-separated list such as --registry local,foton.

--limit <LIMIT>

Maximum number of matching packages to show.

  • Default: 10

--pre-release

Allow matching pre-release versions when searching packages in registries.

Without this option, versions with a suffix such as 1.2.3-rc-1 are ignored.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

foton search <query>
foton search --limit 20 <query>
foton search --registry <registry-id-1>,<registry-id-2> <query>

Output

Each result shows the package name with version and the package registry ID in brackets. If the package has a description, it is printed on the next line.

Example:

example-font@1.2.3 [example]
  Example font family for UI and coding

Notes

  • By default, search does not include pre-release versions. Use --pre-release if you want pre-release versions to be included.
  • Search matches package names and descriptions only.

manifest

Work with package manifest files.

Usage

foton manifest [OPTIONS] <COMMAND>

Commands

  • check: Validate manifest files for installation errors and quality warnings

Typical usage

The manifest command group is mainly intended for package authors. A common workflow is:

  1. Write a manifest file
  2. Run foton manifest check <MANIFEST>
  3. Install the manifest locally with foton install --manifest <MANIFEST>
  4. Add the manifest to a package registry

manifest check

Validate manifest files for installation errors and quality warnings.

Usage

foton manifest check [OPTIONS] <MANIFEST>...

Arguments

<MANIFEST>

Paths to the manifest files to validate.

Options

--no-source-checks

Skip checks that require downloading and examining the source archives or files.

--registry-root <REGISTRY_ROOT>

Treat the given manifest files as belonging to the package registry rooted at this directory.

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

What the command checks

By default, manifest check reads the manifest and then stages it as if it were going to be installed. This includes downloading and examining the source archives or files described by the manifest. Use --no-source-checks to skip those source-dependent checks.

The command reports:

  • installation errors that would prevent the package from being installed
  • quality warnings for common authoring mistakes

Common warnings

For common warning types, see Package Manifest Reference.

Examples

foton manifest check <manifest-path>

Skip source-dependent checks when validating many manifests, for example in a registry:

foton manifest check --no-source-checks <registry-root>\packages\**\manifest.toml

Validate a manifest as part of a package registry rooted at a known directory:

foton manifest check --registry-root <registry-root> <manifest-path>

Treat warnings as errors:

foton --warnings-as-errors manifest check <manifest-path>

Notes

  • This command is primarily intended for package authors.
  • Because the command fetches sources by default, network access may be required.
  • A manifest that parses successfully can still fail manifest check if the sources are invalid or the selected fonts do not install correctly.

font

Work with fonts managed by foton.

Usage

foton font [OPTIONS] <COMMAND>

Commands

  • list: List fonts managed by foton

font list

List fonts managed by foton.

Usage

foton font list [OPTIONS]

Options

--include-system-fonts

Also include all system fonts recognized by Windows.

--include-user-fonts

Also include all user fonts recognized by Windows.

--format <FORMAT>

Select the output format.

  • Default: text
  • Possible values: text, jsonl

Global options

--exit-on-lock

Exit immediately if the package database is locked by another operation.

--no-confirm

Skip interactive confirmation prompts.

--warnings-as-errors

Treat warnings as errors, causing the command to fail if any warning is emitted.

Examples

Show fonts managed by foton:

foton font list

Also show system fonts recognized by Windows:

foton font list --include-system-fonts

Also show user fonts recognized by Windows:

foton font list --include-user-fonts

Also show system and user fonts:

foton font list --include-system-fonts --include-user-fonts

Show fonts as JSON Lines:

foton font list --format jsonl

Output

By default, font list prints text output grouped by font locations. Within each group, each line shows a font family followed by the faces recognized for that family.

Example:

Fonts from Package example-font@1.2.3:
  - Example Font (Bold, Regular)

With --format jsonl, font list writes one JSON object per visible font face in JSON Lines format.

Notes

  • By default, font list shows fonts attributed to foton-managed packages. In uncommon cases, fonts from unknown locations may also appear when Windows cannot attribute their backing files more precisely.
  • --include-system-fonts adds system fonts recognized by Windows.
  • --include-user-fonts adds user fonts recognized by Windows.
  • font list reads the font set recognized by Windows and classifies each visible font by the location of its backing file when possible.