DocumentsAetherPak Project
aetherpak.yaml Configuration Reference
aetherpak.yaml Configuration Reference
Type
Document
Status
Published
Created
Jun 7, 2026
Updated
Jun 9, 2026
Updated by
Dosu Bot

aetherpak.yaml Configuration Reference#

1. Overview#

aetherpak.yaml is the central configuration file for the AetherPak toolchain. It declares every aspect of your Flatpak repository in a single, version-controllable file: which apps to build and publish, where to host them, how to brand the landing page, which linting rules to enforce, and how to map Git branches to Flatpak release channels.

AetherPak is designed so that the configuration file is optional for one-off operations. All parameters can be supplied as CLI flags or AETHERPAK_-prefixed environment variables, and most individual commands will run without any file on disk. The configuration file becomes essential when you need to :

  • Manage multiple apps from a single repository
  • Persist build defaults (ccache, linter settings, remote dependencies) so they don't need to be re-specified on every run
  • Drive the automated plan / release cycle via the aetherpak plan and aetherpak release commands
  • Customize branding for your landing page beyond what workflow inputs allow
  • Define channel mappings that control how Git refs are translated into Flatpak branch names

When used with the AetherPak GitHub Actions reusable workflow , aetherpak.yaml is the primary way to configure multi-app builds. The workflow reads the file via the config: input and orchestrates a parallel build-publish pipeline for all declared apps.

Note: For single-app repositories where you only need to build and publish one manifest, you may not need an aetherpak.yaml at all — the reusable workflow's manifest-path: input is sufficient. Reach for aetherpak.yaml when you need more than the defaults provide.

2. File Location#

Default discovery#

AetherPak looks for the configuration file in the current working directory — for GitHub Actions workflows, this is the root of your repository checkout. It searches for the following names in order :

  1. aetherpak.yaml
  2. aetherpak.yml

If neither is found and no explicit path was given, the CLI continues with built-in defaults (equivalent to an empty config). Commands that require a config — such as aetherpak plan — will report a clear error if no file exists.

Overriding the path#

CLI flag: Pass --config <path> to any command to load a specific file regardless of the current directory :

aetherpak release --config configs/aetherpak-nightly.yaml

Environment variable: Set AETHERPAK_CONFIG to the desired path. This is especially useful in CI environments where you want to vary the config without modifying the command line:

export AETHERPAK_CONFIG=configs/aetherpak-nightly.yaml
aetherpak release

GitHub Actions workflow input: Pass the repository-relative path via the config: input to the reusable workflow :

jobs:
  publish:
    uses: aetherpak/actions/.github/workflows/publish.yml@v3
    with:
      config: aetherpak.yaml # default; can be a different path

If you maintain multiple independent publishing configurations (for example, a stable channel and a nightly channel), you can run two workflow jobs pointing to different config files. Give each a unique concurrency-group: to prevent race conditions on the shared index :

jobs:
  publish-stable:
    uses: aetherpak/actions/.github/workflows/publish.yml@v3
    with:
      config: aetherpak.yaml
      concurrency-group: publish-stable

  publish-nightly:
    uses: aetherpak/actions/.github/workflows/publish.yml@v3
    with:
      config: aetherpak-nightly.yaml
      concurrency-group: publish-nightly

3. Top-level Fields Reference#

These fields appear at the root of aetherpak.yaml and configure global repository behavior .

FieldTypeRequiredDefaultDescription
registrystringNoghcr.ioOCI registry hostname where application layers are pushed. Use ghcr.io for GitHub Container Registry, quay.io for Red Hat Quay, or any OCI-compatible registry.
pages_urlstringNo¹Project Pages URLThe public base URL where the repository index and landing page are served. Used in .flatpakrepo and .flatpakref files.
oci_repositorystringNo$GITHUB_REPOSITORY or remote_nameThe registry path for OCI image storage (e.g., myorg/myrepo). Defaults to the GitHub repository slug in Actions, or falls back to remote_name if set.
remote_namestringNo<owner>-<repo>The Flatpak remote name users will add with flatpak remote-add. Also the base name for the generated .flatpakrepo file.
repo_titlestringNoFlatpak RepositoryHuman-readable title displayed on the landing page and embedded in .flatpakrepo metadata.
repo_homepagestringNoURL for the repository's homepage, embedded in .flatpakrepo metadata.
runtime_repostringNoFlathub URLThe .flatpakrepo URL used to resolve runtime dependencies. Defaults to https://flathub.org/repo/flathub.flatpakrepo.
no_signbooleanNofalseWhen true, disables GPG signing entirely. Use this only in development — signed repositories provide cryptographic verification for users.
output_dirstringNoBase directory for all build outputs (state, site, records, ccache). When set, per-app state_dir and ccache_dir default to subdirectories under this path.
channel_mappingsmap[string]stringNoMaps Git ref names or glob patterns to Flatpak branch names. See Section 7 for full details.
defaultsobjectNoInheritable build defaults applied to all apps. See Section 4.
brandingobjectNoLanding page customization. See Section 5.
linterobjectNoGlobal linter configuration. See Section 6.
appslistYes²List of application definitions. See Section 8.

¹ pages_url is technically optional in the config (it can be supplied as the pages-url workflow input or AETHERPAK_PAGES_URL environment variable), but it must be resolved to a value before publishing. The generated .flatpakrepo and .flatpakref files will be incorrect without it.

² apps is required in config-mode operation. In manifest-mode (using --manifest on the CLI or manifest-path: in workflows), you do not need an apps list.

Examples#

registry: ghcr.io
pages_url: https://myorg.github.io/myrepo
oci_repository: myorg/myrepo
remote_name: myorg-apps
repo_title: "My Application Repository"
repo_homepage: https://myorg.example.org
# Custom registry (Quay.io)
registry: quay.io
oci_repository: myorg/flatpak-apps
pages_url: https://apps.myorg.example.org

4. defaults Section#

The defaults block sets inheritable build settings that are applied to every app in the apps list. Individual apps can override or extend these settings .

defaults:
  ccache: true
  ccache_dir: .ccache
  state_dir: .state
  run_linter: true
  builder_args:
    - --install-deps-from=flathub
  remotes:
    flathub: https://flathub.org/repo/flathub.flatpakrepo
    custom_repo:
      url: https://example.com/repo.flatpakrepo
      gpg_verify: true
      gpg_key: https://example.com/keys/gpg.key
      sig_verify_url: https://example.com/signatures
  flatpaks:
    - remote: flathub
      ref: org.freedesktop.Sdk.Extension.rust-stable

Fields#

FieldTypeDefaultDescription
ccachebooleanfalseEnable the compiler cache for all builds. Speeds up repeated builds significantly.
ccache_dirstring.ccacheDirectory for the compiler cache. If output_dir is set at the top level, defaults to <output_dir>/.ccache.
state_dirstring.stateDirectory for flatpak-builder state (SDK caches, dependencies). If output_dir is set, defaults to <output_dir>/.state.
run_linterbooleanfalseRun flatpak-builder-lint for all apps by default. Can be overridden per app.
builder_argslist[string][]Extra command-line arguments passed to flatpak-builder. Applied to all apps; replaced entirely if an app specifies its own builder_args. Note: As of v0.21.0, the CLI automatically injects --install-deps-from flags for each configured remote in the remotes section, so explicitly specifying --install-deps-from in builder_args is typically unnecessary.
no_install_depsbooleanfalseDisable automatic injection of --install-deps-from flags when running flatpak-builder.
no_flathubbooleanfalseDisable automatic injection of the flathub remote as a dependency source.
remotesmap[string, string | RemoteConfig]{}Additional Flatpak remotes to configure before building. Each entry can be a simple URL string or an exploded RemoteConfig object with fields: url (string, required), gpg_verify (boolean, optional), gpg_key (string, optional - can be a local path, URL, or inline ASCII-armored GPG public key block), sig_verify_url (string, optional). Merged into each app's remotes; app values override on name conflicts.
flatpakslist[{remote, ref}][]Flatpak packages to install before building, referenced by remote name and ref. Prepended to each app's flatpaks list; duplicates are removed.

Inheritance behavior#

AetherPak's Normalize() function applies defaults to each app according to the following rules :

ccache — Inherited if the app does not explicitly set ccache. If set in defaults but not in an app entry, the app uses the defaults value.

ccache_dir and state_dir — Inherited from defaults if the app does not specify its own value. If output_dir is set at the top level, the defaults are <output_dir>/.ccache and <output_dir>/.state respectively.

run_linter — If defaults.run_linter is true and an app does not explicitly enable linting, the app inherits true. (Inheritance is one-directional: a false default does not force apps to disable linting if they individually enable it.)

builder_args — The defaults list is copied to any app whose builder_args list is empty. If an app specifies any builder_args, the defaults list is not merged — the app's list takes over entirely .

remotes — The defaults map is merged into each app's remotes map. If both defaults and the app define a remote with the same name, the app's definition wins .

flatpaks — The defaults list is prepended to each app's flatpaks list, and duplicates (by remote + ref pair) are removed .

Tip: Use defaults when most of your apps share the same build configuration. Declare the common settings once in defaults, then only specify overrides in individual app entries.

Example: enabling linting globally, disabling for one app#

defaults:
  run_linter: true # Lint every app by default
  remotes:
    flathub: https://flathub.org/repo/flathub.flatpakrepo

apps:
  - id: org.example.AppOne
    manifest: apps/AppOne/manifest.json
    # Inherits run_linter: true from defaults

  - id: org.example.AppTwo
    manifest: apps/AppTwo/manifest.json
    run_linter: false # Override: skip linting for this app

Note: The --install-deps-from=flathub flag is automatically injected by the CLI when flathub is configured in the remotes section (as of v0.21.0), so it no longer needs to be specified in builder_args.

5. branding Section#

The branding block customizes the appearance of the generated landing page (index.html) without requiring a fully custom template. All fields are optional — omitting the entire branding section produces a clean default page .

branding:
  logo_url: https://example.org/logo.png
  favicon_url: https://example.org/favicon.ico
  accent_color: "#3584e4"
  footer_text: "© 2026 My Project. Licensed under GPL-3.0."
  index_template: .github/templates/index.html

Fields#

FieldTypeDescription
logo_urlstringFull URL to your project's logo image. Displayed prominently on the landing page header.
favicon_urlstringFull URL to a favicon image. Used as the browser tab icon.
accent_colorstringPrimary accent color as a CSS hex value (e.g., "#3584e4"). Applied to buttons, links, and highlighted UI elements on the landing page.
footer_textstringText displayed in the landing page footer. HTML is supported, allowing links, bold text, and other inline markup.
index_templatestringRepository-relative path to a custom Jinja2/Go template file that replaces the default landing page. See note below.

Custom templates (index_template)#

For complete control over the landing page, provide a custom template via branding.index_template . The path is resolved relative to the repository root when aetherpak build-site runs.

branding:
  index_template: .github/templates/index.html

Note: index_template can also be supplied as the index-template: workflow input or the --index-template CLI flag. These override the value set in aetherpak.yaml. For the full precedence order and a complete guide to template variables, see Custom Index Landing Page Templates .

Workflow input overrides#

The branding fields set in aetherpak.yaml can be overridden for specific workflow runs using workflow inputs :

Workflow inputOverrides
index-templatebranding.index_template
remote-nameremote_name (top-level)
pages-urlpages_url (top-level)
landing-page: falseSkips index.html generation entirely

6. linter Section#

The top-level linter block configures flatpak-builder-lint behavior globally across all apps. Individual app entries can extend this configuration with additional exceptions .

linter:
  strict: false
  ignore_rules:
    - no-appstream-screenshot-urls
    - appstream-screenshot-missing
  exceptions_file: .linter-exceptions.json

Fields#

FieldTypeDefaultDescription
strictbooleantrueWhen true, any lint failure causes the build to fail immediately. When false, failures are logged as warnings but the build continues.
ignore_ruleslist[string][]List of flatpak-builder-lint rule IDs to suppress globally.
exceptionslist[string][]Alias for ignore_rules. Both lists are merged and deduplicated before use — you may use either or both .
exceptions_filestringRepository-relative path to a JSON exceptions file. Uses the same format as upstream flatpak-builder-lint, making it easy to reuse exception files from Flathub workflows.

Note: ignore_rules and exceptions are functionally identical. They exist as aliases to accommodate different naming conventions. At runtime, both lists are merged, deduplicated, and treated as a single set of suppressed rules .

Strict mode and defaults#

The strict field defaults to true when the linter is enabled, meaning any lint violation fails the build. For repositories that want to run the linter for visibility without blocking on every warning, set strict: false:

linter:
  strict: false # Warn but don't fail
  ignore_rules:
    - appstream-screenshot-missing

Enabling the linter globally#

The linter block configures how the linter behaves; whether it runs at all is controlled by defaults.run_linter (or per-app run_linter). To enable linting for all apps with consistent settings:

defaults:
  run_linter: true

linter:
  strict: false
  ignore_rules:
    - no-appstream-screenshot-urls

Per-app overrides#

Apps can define their own linter block that extends (not replaces) the global configuration . Per-app exceptions are additive: they are merged with global exceptions before each lint invocation. For the full per-app linter reference, see Section 8: apps Entries.

See also: Working with Linter Exceptions for a comprehensive guide covering exception file formats, per-app configuration, CLI flags, and environment variable overrides .

7. channel_mappings#

The channel_mappings field controls how Git references (branches and tags) are transformed into Flatpak branch names at publish time . This configuration lets you customize the deployment channel without changing your Git workflow — for example, mapping version tags to the stable channel, main to beta, and feature branches to nightly or custom channel names .

Purpose and behavior#

Without channel mappings, AetherPak applies a base resolution strategy :

  • Tags resolve to stable
  • Default branch (e.g. main) resolves to beta
  • Any other branch resolves to the Git ref name itself

The channel_mappings dictionary overrides these defaults by mapping Git ref patterns to Flatpak branch names. Mappings support both exact matches and glob patterns, and are applied in a specific lookup order to handle overlapping patterns predictably .

Lookup order#

When resolving a Git ref to a Flatpak channel, AetherPak performs four lookups in sequence and uses the first match :

  1. Exact match on ref name — if channel_mappings["main"] exists and the ref name is main, use that mapping
  2. Glob match on ref name — if a pattern like release/* matches the ref name via filepath.Match(), use the mapping
  3. Exact match on base channel — if the base resolution produces stable and channel_mappings["stable"] exists, use that mapping
  4. Glob match on base channel — if a pattern matches the base-resolved channel, use the mapping

This order allows you to rename channels, route specific branches, or apply wildcards to branch families.

Pattern precedence#

When multiple glob patterns match the same ref or base channel, AetherPak applies the following tie-breaking rules :

  1. Longest pattern first — the pattern with the most characters wins
  2. Alphabetical order — if two patterns have the same length, the lexicographically smaller pattern wins

This ensures deterministic and predictable behavior when overlapping wildcards are defined.

Glob syntax#

Patterns use Go's filepath.Match() syntax :

PatternMatches
*Any sequence of characters in a single path segment
?Any single character
[abc]Any character in the set a, b, or c
[a-z]Any character in the range a to z

Note: Double-star (**) is not supported — * does not match /, so patterns like release/* only match single-level paths like release/1.0, not release/1.0/patch.

Patterns are validated at configuration load time using filepath.Match() . Invalid patterns cause the configuration to fail validation and stop the workflow.

Common examples#

Example 1: Map default branch to beta#

channel_mappings:
  main: beta

Pushes to main publish to the beta channel instead of the default stable .

Example 2: Version tags and releases to stable#

channel_mappings:
  "v*": stable
  "release/*": stable
  • Tags matching v* (e.g. v1.0.0, v2.1.3) publish to stable
  • Branches matching release/* (e.g. release/1.0, release-2.1) publish to stable

Example 3: Multi-channel deployment#

channel_mappings:
  main: beta
  "staging/*": alpha
  "release-*": stable
  stable: prod
  • mainbeta
  • staging/patch-1alpha
  • release-1.0.0stable
  • Tags (base resolution: stable) → prod via the stable: prod mapping

This configuration renames the stable channel to prod for tagged releases while routing development branches to dedicated channels .

Example 4: Feature branches to nightly#

channel_mappings:
  main: stable
  "feature-*": nightly
  • main publishes as stable
  • Any branch starting with feature- publishes as nightly

Impact on published artifacts#

The channel name determined by channel_mappings affects:

  1. OCI image tag — images are tagged as <app-id>-<branch>-<arch>, so changing the channel changes the tag under which the image is pushed
  2. Flatpak branch — the org.flatpak.ref label embedded in the OCI image and the index/static entry both use the resolved channel as the branch name
  3. Generated .flatpakref files — each file is named <app-id>-<channel>.flatpakref and embeds Branch=<channel> in its content
  4. Installation commands — the landing page displays flatpak install <remote> <app-id>//<channel> using the resolved channel

Global vs per-app scope#

channel_mappings is a global field in aetherpak.yaml . All apps in a multi-app repository use the same mappings.

If an app declares an explicit branch field in its apps entry , that branch name is used as-is and overrides both base resolution and channel_mappings. This per-app override is useful for third-party bundles with fixed channels or when you want one app to always publish to a specific branch regardless of the Git ref .

Pattern validation#

Invalid glob patterns cause configuration validation to fail at load time :

channel_mappings:
  "[invalid": stable # unmatched bracket → validation error

Run aetherpak plan --config aetherpak.yaml locally to validate your patterns before pushing.

Use cases#

  • Renaming default channels: map stableproduction to align with your team's terminology
  • Multi-stage deployments: separate alpha, beta, and stable channels driven by branch naming conventions
  • Third-party bundle ingestion: remap upstream channels (e.g. master) to your own naming scheme
  • Custom branch workflows: route experimental branches to a nightly channel without changing your CI triggers

8. apps Entries#

The apps list is the core of the AetherPak configuration file. Each entry defines a Flatpak application to build or publish, its source (manifest or pre-built bundles), target architectures, and build/lint overrides .

Field Reference#

FieldTypeRequiredDescription
idstringYesReverse-DNS Flatpak application identifier (e.g., org.example.App). Must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$ .
manifeststringConditionalRelative path to the Flatpak manifest file. Mutually exclusive with bundles .
bundlesmap[string]BundleConditionalMap of architecture → {url, sha256} for pre-built .flatpak bundles. Mutually exclusive with manifest .
arches[]stringNoTarget architectures (x86_64, aarch64). Defaults to [x86_64] for manifest apps .
branchstringNoFlatpak branch/channel name. If not set, automatically resolved from the manifest file's branch field (if present), otherwise defaults to stable. Must match ^[A-Za-z0-9._-]+$ .
run_linter / run-linterboolNoOverride global linting setting for this app. Both kebab-case and snake_case are accepted .
ccache*boolNoOverride ccache setting from defaults. Inherits from defaults.ccache if not set .
ccache_dirstringNoOverride ccache directory. Inherits from defaults.ccache_dir or defaults to .ccache .
state_dirstringNoOverride state directory. Inherits from defaults.state_dir or defaults to .state .
builder_args[]stringNoAdditional flatpak-builder arguments. Replaces defaults.builder_args if non-empty . Note: As of v0.21.0, --install-deps-from flags are auto-injected for remotes configured in the remotes section.
no_install_deps*boolNoOverride defaults.no_install_deps. Disable automatic injection of --install-deps-from flags when running flatpak-builder.
no_flathub*boolNoOverride defaults.no_flathub. Disable automatic injection of the flathub remote as a dependency source.
remotesmap[string, string | RemoteConfig]NoAdditional Flatpak remotes. Each entry can be a simple URL string or an exploded RemoteConfig object with fields: url (string, required), gpg_verify (boolean, optional), gpg_key (string, optional - can be a local path, URL, or inline ASCII-armored GPG public key block), sig_verify_url (string, optional). Merged with defaults.remotes, app values override conflicts .
flatpaks[]FlatpakDepNoAdditional Flatpak dependencies to pre-install ({remote, ref}). Prepended to defaults.flatpaks, deduplicated .
linter*LinterConfigNoPer-app linter config (strict, ignore_rules, exceptions, exceptions_file). Additive to global linter config .
runtimestringDeprecatedDeprecated field, no longer used .
runtime-versionstringDeprecatedDeprecated field, no longer used .

Note: Each app entry must specify exactly one of manifest or bundles — not both .

Source: manifest vs bundles#

Apps can be built from source via a Flatpak manifest or imported from pre-built .flatpak bundles.

manifest#

The manifest field points to a relative path to a Flatpak manifest (JSON or YAML format) :

apps:
  - id: org.example.MyApp
    manifest: apps/org.example.MyApp/org.example.MyApp.json
    arches: [x86_64, aarch64]
    branch: stable

Validation rules :

  • Must be a relative path (no absolute paths starting with /)
  • Must not contain .. segments (no path traversal)

bundles#

The bundles field maps architectures to pre-built bundle URLs and checksums :

apps:
  - id: com.example.ThirdPartyApp
    bundles:
      x86_64:
        url: https://upstream.example.com/app_x86_64.flatpak
        sha256: "abc123...64hexchars"
      aarch64:
        url: https://upstream.example.com/app_aarch64.flatpak
        sha256: "def456...64hexchars"
    branch: stable

Bundle validation rules :

  • Only x86_64 and aarch64 architectures are supported
  • url must start with http:// or https://
  • sha256 must be exactly 64 lowercase hexadecimal characters
  • Both url and sha256 are required for each architecture

Use bundles for third-party upstream apps or pre-built binaries from other build systems .

Architectures and Branches#

arches#

The arches field specifies target architectures for the app . Supported values are x86_64 and aarch64 . For manifest-based apps, defaults to [x86_64] .

apps:
  - id: org.example.App
    manifest: app.json
    arches: [x86_64, aarch64] # Build for both architectures

branch#

The branch field sets the Flatpak branch name, which determines the release channel . Must match the pattern ^[A-Za-z0-9._-]+$ .

apps:
  - id: org.example.App
    manifest: app.json
    branch: beta # Publish to the 'beta' branch

Branch resolution order:

When branch is not explicitly set in aetherpak.yaml, AetherPak attempts to resolve it automatically :

  1. If the manifest file path is specified, parse the manifest and use the branch field from the Flatpak manifest if it exists
  2. If no branch is found in the manifest (or no manifest is specified), default to stable

This automatic resolution is useful when the Flatpak manifest already declares a branch (e.g., for nightly builds or beta channels), allowing you to avoid duplicating that value in aetherpak.yaml.

Branch names affect how users install the app and are visible in .flatpakref files. The global channel_mappings configuration can automatically map git branches to Flatpak branch names.

Build and Linter Overrides#

run_linter / run-linter#

Both run_linter (snake_case) and run-linter (kebab-case) are accepted and control whether linting runs for this app . If defaults.run_linter is true and the app does not explicitly disable it, linting is inherited .

defaults:
  run_linter: true

apps:
  - id: org.example.AppOne
    manifest: app1.json
    run-linter: false # Disable linting for this app

  - id: org.example.AppTwo
    manifest: app2.json
    # Inherits run_linter: true from defaults

linter#

Per-app linter configuration overrides or extends the global linter block . Supported fields: strict, ignore_rules, exceptions, exceptions_file .

linter:
  strict: true
  exceptions:
    - global-exception

apps:
  - id: org.example.App
    manifest: app.json
    linter:
      strict: false # Override strictness for this app
      exceptions:
        - app-specific-exception # Merged with global exceptions
      exceptions_file: app-exceptions.json # Overrides global file

Behavior:

  • strict: Overrides global strict mode
  • ignore_rules and exceptions: Merged with global lists (additive)
  • exceptions_file: Overrides global exceptions file if specified

ccache, ccache_dir, state_dir#

These fields override build caching and state storage :

defaults:
  ccache: true
  ccache_dir: .ccache
  state_dir: .state

apps:
  - id: org.example.App
    manifest: app.json
    ccache: false # Disable ccache for this app
    state_dir: custom-state # Use custom state directory

Inheritance rules :

  • ccache: Inherits from defaults.ccache if not set
  • ccache_dir: Inherits from defaults.ccache_dir, defaults to .ccache (or <output_dir>/.ccache)
  • state_dir: Inherits from defaults.state_dir, defaults to .state (or <output_dir>/.state)

builder_args#

Additional arguments passed to flatpak-builder . If the app specifies builder_args, they replace (not merge with) defaults.builder_args .

defaults:
  remotes:
    flathub: https://flathub.org/repo/flathub.flatpakrepo

apps:
  - id: org.example.App
    manifest: app.json
    builder_args:
      - --disable-updates # Replaces defaults.builder_args entirely

Note: As of v0.21.0, the CLI automatically injects --install-deps-from flags for each remote configured in the remotes section, so you typically do not need to manually specify --install-deps-from in builder_args unless you want to control this behavior explicitly.

Remotes and Dependencies#

remotes#

Additional Flatpak remotes to add before building . Each entry maps a remote name to either a simple flatpakrepo URL string or an exploded RemoteConfig object. Merged with defaults.remotes, with app values overriding conflicts .

defaults:
  remotes:
    flathub: https://flathub.org/repo/flathub.flatpakrepo

apps:
  - id: org.example.App
    manifest: app.json
    remotes:
      flathub-beta: https://flathub.org/beta-repo/flathub-beta.flatpakrepo
      custom_repo:
        url: https://example.com/repo.flatpakrepo
        gpg_verify: true
        gpg_key: https://example.com/keys/gpg.key
        sig_verify_url: https://example.com/signatures
      # flathub from defaults is also included

RemoteConfig fields:

  • url (string, required): The flatpakrepo URL
  • gpg_verify (boolean, optional): Enable/disable GPG verification for this remote
  • gpg_key (string, optional): GPG public key (can be a local path, URL, or inline ASCII-armored GPG public key block)
  • sig_verify_url (string, optional): Signature lookaside URL

Validation rules :

  • Remote names and URLs must be non-empty
  • URLs must start with http:// or https://

flatpaks#

Flatpak dependencies to pre-install before building . Each entry requires remote and ref fields. App dependencies are appended to defaults.flatpaks with deduplication .

defaults:
  flatpaks:
    - remote: flathub
      ref: org.freedesktop.Platform//23.08

apps:
  - id: org.example.App
    manifest: app.json
    flatpaks:
      - remote: flathub
        ref: org.freedesktop.Sdk.Extension.llvm17//23.08
      # org.freedesktop.Platform from defaults is also included

Validation rules :

  • remote must be non-empty
  • ref must be non-empty

Complete Example#

# aetherpak.yaml

defaults:
  ccache: true
  run_linter: true
  remotes:
    flathub: https://flathub.org/repo/flathub.flatpakrepo
  flatpaks:
    - remote: flathub
      ref: org.freedesktop.Platform//23.08

linter:
  strict: true
  exceptions:
    - appstream-screenshot-missing

apps:
  # Manifest-based app with overrides
  - id: org.example.AppOne
    manifest: apps/org.example.AppOne/app.json
    arches: [x86_64, aarch64]
    branch: stable
    run-linter: true
    ccache: true
    builder_args:
      - --disable-updates
    linter:
      strict: false
      exceptions:
        - app-specific-exception

  # Bundle-based app
  - id: com.example.ThirdPartyApp
    bundles:
      x86_64:
        url: https://upstream.example.com/app_x86_64.flatpak
        sha256: "abc123def456...64hexchars"
      aarch64:
        url: https://upstream.example.com/app_aarch64.flatpak
        sha256: "789012fed654...64hexchars"
    branch: stable
    run_linter: false

  # Minimal manifest app (inherits defaults)
  - id: org.example.AppTwo
    manifest: apps/org.example.AppTwo/app.json
    # Inherits: arches=[x86_64], branch=stable, run_linter=true, ccache=true

Note: The --install-deps-from=flathub flag is automatically injected by the CLI (as of v0.21.0) when flathub is configured in the remotes section, so it no longer needs to be specified in builder_args.

9. bundles Map#

The bundles field allows you to ingest pre-built Flatpak bundles (.flatpak files) instead of building from a manifest. This is useful for third-party apps, vendor-provided binaries, or situations where you want to distribute an existing .flatpak file through your repository.

Structure#

bundles is a map from architecture name to a Bundle object containing a URL and SHA-256 checksum :

apps:
  - id: com.example.ThirdPartyApp
    branch: stable
    bundles:
      x86_64:
        url: https://releases.example.com/ThirdPartyApp_x86_64.flatpak
        sha256: "2159fc643175dcf54f8b9293f48fb8b11c41a87fe31684ee4c5d4e94c6f37a42"
      aarch64:
        url: https://releases.example.com/ThirdPartyApp_aarch64.flatpak
        sha256: "a7b3c9d2e1f04586738c9d2b4f1e3a6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2"

Fields#

Each bundle entry has two required fields :

  • url: string, required. Must start with http:// or https:// .
  • sha256: string, required. Must be exactly 64 lowercase hexadecimal characters .

Supported Architectures#

Only x86_64 and aarch64 are supported as bundle keys . Apps using bundles do not need to set the arches field separately; architectures are derived from the keys in the bundles map .

Rebind Behavior#

AetherPak downloads each bundle, verifies its SHA-256 checksum, imports it into an OSTree repository, and rebinds the commit to the declared branch name . This rebind step is critical because upstream bundles often embed the wrong branch name (such as app/<id>/<arch>/master). The prep-bundle action rewrites both the ref name and the xa.ref binding to match your declared branch, ensuring that flatpak install accepts the bundle correctly.

When to Use bundles vs manifest#

  • Use bundles when:

    • You are redistributing a third-party app where you don't control the source.
    • An upstream vendor provides ready-made .flatpak files.
    • Your build framework (e.g., Electron or Tauri) produces a .flatpak directly.
    • You want to mirror another project's .flatpak from GitHub Releases or another artifact store.
  • Use manifest when:

    • You are building your own app from source.
    • You need control over build options, dependencies, or the build environment.
    • You want to customize the Flatpak manifest for your application.

An app must specify exactly one of manifest or bundles .

Adding Bundles with the CLI#

The aetherpak add --bundle-url <url> command automatically downloads the bundle, computes its SHA-256, and adds it to your aetherpak.yaml, saving manual checksum calculation.

SHA-256 Update Warning#

If the upstream URL points to a static location that is updated over time (e.g., a "latest" URL), the SHA-256 checksum in your configuration must be updated whenever the file changes. AetherPak will fail verification if the downloaded file does not match the declared checksum.

Example with Both Architectures#

apps:
  - id: com.example.ThirdPartyApp
    branch: stable
    bundles:
      x86_64:
        url: https://releases.example.com/ThirdPartyApp_x86_64.flatpak
        sha256: "2159fc643175dcf54f8b9293f48fb8b11c41a87fe31684ee4c5d4e94c6f37a42"
      aarch64:
        url: https://releases.example.com/ThirdPartyApp_aarch64.flatpak
        sha256: "a7b3c9d2e1f04586738c9d2b4f1e3a6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2"

10. Validation Rules#

AetherPak validates all configuration at load time to catch errors early and provide clear feedback. Validation checks ensure that app IDs follow the correct format, paths are safe, URLs are well-formed, and required fields are present. If validation fails, the CLI reports the specific error and exits before any build or release operation begins .

App ID#

The id field is required for every app and must follow a strict format .

Requirements:

  • Cannot be empty
  • Must match the regular expression: ^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$

Rules:

  • First character must be alphanumeric (A-Z, a-z, or 0-9)
  • Remaining characters (up to 254 additional characters) can be alphanumeric, dot (.), underscore (_), or hyphen (-)
  • Maximum total length: 255 characters

Valid examples:

  • org.gnome.Sudoku
  • com.example.App
  • io.github.MyApp

Invalid examples:

  • .BadStart — cannot start with a dot
  • my app — spaces not allowed
  • app/id — slashes not allowed
  • A string with more than 255 characters

Branch#

The branch field must follow a simple naming pattern .

Requirements:

  • Must match the regular expression: ^[A-Za-z0-9._-]+$

Rules:

  • Only alphanumeric characters, dots (.), underscores (_), and hyphens (-) are allowed
  • No slashes or spaces
  • Cannot be empty (defaults to "stable" if not specified)

Valid examples:

  • stable
  • beta
  • 1.0
  • my-branch
  • v2.0-rc1

Invalid examples:

  • my/branch — slashes not allowed
  • my branch — spaces not allowed

Architectures#

AetherPak supports only two architectures .

Supported architectures:

  • x86_64
  • aarch64

Any other value will cause a validation error. This applies to both the arches list for manifest-based apps and the keys in the bundles map for bundle-based apps.

Error example:

app "org.example.App": unsupported arch "arm"

Manifest vs Bundles#

Every app must specify exactly one of manifest or bundles .

Rules:

  • If both are set, validation fails
  • If neither is set, validation fails
  • Only one source type is allowed per app

Error examples:

app "org.example.App": exactly one of 'manifest' or 'bundles' is required

Manifest Path#

When using a manifest field, the path must be relative and safe .

Requirements:

  • Must be a relative path
  • Cannot start with / (no absolute paths)
  • Cannot contain path traversal segments (..)

Valid examples:

  • flatpak-manifest.json
  • manifests/org.example.App.json
  • build/manifest.yaml

Invalid examples:

  • /etc/manifest.json — absolute path not allowed
  • ../manifests/app.json — path traversal not allowed
  • ../../etc/passwd — path traversal not allowed

Error examples:

app "org.example.App": 'manifest' must be a relative path, cannot be absolute
app "org.example.App": 'manifest' must be a relative path with no '..' segments

Bundle URL and SHA256#

When using bundles, each architecture entry must include both a url and a sha256 field .

Requirements:

  • Architecture key must be a supported architecture (x86_64 or aarch64)
  • url is required and must start with http:// or https://
  • sha256 is required and must be exactly 64 lowercase hexadecimal characters

Valid example:

bundles:
  x86_64:
    url: https://example.com/app.flatpak
    sha256: a3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Invalid examples:

  • ftp://example.com/app.flatpak — only HTTP/HTTPS allowed
  • A3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 — uppercase not allowed
  • a3b0c442 — too short (must be exactly 64 characters)

Error examples:

app "org.example.App": unsupported bundle arch "arm64"
app "org.example.App" bundle "x86_64": 'url' and 'sha256' are required
app "org.example.App" bundle "x86_64": 'url' must start with http:// or https://
app "org.example.App" bundle "x86_64": 'sha256' must be 64 lowercase hex characters

Remote Validation#

Both the defaults.remotes and per-app remotes fields are validated .

Requirements:

  • Remote name cannot be empty
  • Remote URL cannot be empty
  • Remote URL must start with http:// or https://

Valid example:

remotes:
  flathub-beta: https://flathub.org/beta-repo/flathub-beta.flatpakrepo

Invalid examples:

  • Empty name as a map key
  • Empty string as URL value
  • ftp://example.com/repo.flatpakrepo — only HTTP/HTTPS allowed

Error examples:

app "org.example.App": flatpak remote name cannot be empty
app "org.example.App": flatpak remote "my-remote" URL cannot be empty
app "org.example.App": flatpak remote "my-remote" URL "ftp://..." must start with http:// or https://

Flatpak Dependency Validation#

Both the defaults.flatpaks and per-app flatpaks fields are validated .

Requirements:

  • remote field cannot be empty
  • ref field cannot be empty

Valid example:

flatpaks:
  - remote: flathub
    ref: org.freedesktop.Platform//23.08

Invalid examples:

  • Missing remote field
  • Missing ref field
  • Empty string for either field

Error examples:

app "org.example.App": flatpak dependency remote cannot be empty
app "org.example.App": flatpak dependency ref cannot be empty

Channel Mappings Pattern Validation#

The channel_mappings field uses glob patterns that are validated at config load time .

Requirements:

  • Each pattern key must be valid glob syntax according to Go's filepath.Match function

Valid examples:

  • main — exact match
  • release/* — glob wildcard
  • v* — prefix glob

Invalid examples:

  • [ — unmatched bracket
  • release/[abc — unclosed character class

Error example:

invalid channel mapping pattern "release/[abc": syntax error in pattern

Warning: Pattern validation occurs at load time. Invalid patterns prevent the entire configuration from loading, so test your patterns carefully.

Common Validation Errors#

Error MessageCauseFix
app entry missing 'id'The id field is empty or not setAdd a valid reverse-DNS app ID
'id' must match format ...App ID contains invalid characters or formatUse only alphanumeric, ., _, -, and start with alphanumeric
'branch' must match format ...Branch name contains slashes or spacesUse only alphanumeric, ., _, -
unsupported arch "arm"Architecture is not x86_64 or aarch64Use only x86_64 or aarch64
exactly one of 'manifest' or 'bundles' is requiredBoth or neither are specifiedSpecify exactly one: either manifest or bundles
'manifest' must be a relative pathPath starts with / or contains ..Use a relative path without traversal
'url' must start with http:// or https://Bundle URL uses unsupported protocolUse HTTP or HTTPS only
'sha256' must be 64 lowercase hex charactersSHA256 is wrong length or has uppercaseUse exactly 64 lowercase hex characters
flatpak remote name cannot be emptyRemote map has empty keyProvide a non-empty remote name
flatpak dependency ref cannot be emptyFlatpak ref is missing or emptyProvide a valid Flatpak ref string
invalid channel mapping patternGlob syntax errorFix the glob pattern syntax

11. Precedence and Overrides#

AetherPak configuration follows a well-defined precedence hierarchy, allowing you to set defaults in aetherpak.yaml while selectively overriding them for specific environments or one-off operations. Understanding this hierarchy is essential for managing multi-environment workflows and debugging configuration issues.

Configuration Precedence Stack#

Configuration values are resolved from multiple sources in the following order, from highest to lowest priority :

  1. CLI flags — Explicit command-line flags like --config, --output-dir, --run-linter
  2. Environment variables — Variables with the AETHERPAK_ prefix (and special cases OCI_USERNAME, OCI_PASSWORD)
  3. Configuration file — Settings in aetherpak.yaml or the file specified by --config
  4. Built-in defaults — Fallback values applied by the Normalize() method when no other source provides a value

Higher-priority sources always override lower-priority ones. For example, --output-dir=/tmp/build on the command line takes precedence over output_dir: dist in aetherpak.yaml, which in turn overrides the built-in default.

Where GitHub Actions Fit#

GitHub Actions workflow inputs translate directly to environment variables with the AETHERPAK_ prefix . This means workflow inputs sit at precedence level 2 — higher than the config file but lower than explicit CLI flags.

Practical implication: If you pass config: staging.yaml as a workflow input but then run a custom step with aetherpak build --config production.yaml, the CLI flag wins. This allows debugging and selective overrides even within automated workflows.

Environment Variable Mapping#

The CLI uses Viper for configuration management with the following mapping rules :

  • All environment variables are prefixed with AETHERPAK_
  • Both . (dot) and - (hyphen) in config keys are replaced with _ (underscore) for environment variables
  • Environment variable names are case-insensitive but conventionally uppercase

Examples:

Config KeyEnvironment Variable
registryAETHERPAK_REGISTRY
pages_urlAETHERPAK_PAGES_URL
oci_repositoryAETHERPAK_OCI_REPOSITORY
remote_nameAETHERPAK_REMOTE_NAME
output_dirAETHERPAK_OUTPUT_DIR
branding.index_templateAETHERPAK_BRANDING_INDEX_TEMPLATE
linter.strictAETHERPAK_LINTER_STRICT
channel_mappings.mainAETHERPAK_CHANNEL_MAPPINGS_MAIN

Special cases without the prefix :

  • OCI_USERNAMEoci_username
  • OCI_PASSWORDoci_password

These are non-prefixed for compatibility with standard container registry authentication patterns.

Fields That Can Be Overridden#

The following table shows which configuration fields support override from environment variables or CLI flags:

FieldEnvironment VariableCLI FlagNotes
registryAETHERPAK_REGISTRYOCI registry host
pages_urlAETHERPAK_PAGES_URLPublic hosting URL
oci_repositoryAETHERPAK_OCI_REPOSITORYRegistry image path
remote_nameAETHERPAK_REMOTE_NAMEFlatpak remote name
output_dirAETHERPAK_OUTPUT_DIR--output-dirBase directory for artifacts
defaults.run_linterAETHERPAK_DEFAULTS_RUN_LINTER--run-linterEnable/disable linting
defaults.ccache_dirAETHERPAK_DEFAULTS_CCACHE_DIR--ccache-dirccache directory
defaults.state_dirAETHERPAK_DEFAULTS_STATE_DIR--state-dirState directory
defaults.builder_argsAETHERPAK_DEFAULTS_BUILDER_ARGS--builder-argExtra flatpak-builder flags
defaults.remotes--flatpak-remoteAdditional Flatpak remotes
defaults.flatpaks--flatpak-depAdditional packages to install
linter.exceptionsAETHERPAK_LINTER_EXCEPTIONS--linter-exceptionLinter rule suppressions
linter.exceptions_fileAETHERPAK_LINTER_EXCEPTIONS_FILE--linter-exceptions-filePath to exceptions JSON file
branding.index_templateAETHERPAK_INDEX_TEMPLATE--index-templateCustom landing page template

Fields That Cannot Be Overridden#

Some configuration fields must be defined in aetherpak.yaml and cannot be overridden from the environment or CLI:

  • apps list — The registry of applications must be in the config file
  • defaults section — While individual defaults can be overridden (see table above), the defaults block itself must be in the config
  • branding section — Only index_template can be overridden; other branding fields like logo_url, accent_color, footer_text must be in the config
  • channel_mappings patterns — Individual mappings can be set via AETHERPAK_CHANNEL_MAPPINGS_<KEY>, but the full pattern set is best managed in the config file

Override Examples#

Example 1: Using a Staging Registry#

Override the registry for a staging deployment without modifying aetherpak.yaml:

AETHERPAK_REGISTRY=ghcr.io/myorg/staging aetherpak plan

This builds the plan using the staging registry while keeping all other config values from aetherpak.yaml.

Example 2: Per-Environment Output Directories#

Use different output directories for local development vs. CI:

# aetherpak.yaml
output_dir: dist
# Local development — override to keep builds isolated
aetherpak build --output-dir=/tmp/aetherpak-builds

# CI — use the config default (dist)
aetherpak build

Example 3: Suppressing Linter Rules in CI#

Temporarily suppress a linter rule for a known issue without editing the config :

aetherpak build \
  --manifest apps/org.example.App/manifest.json \
  --run-linter \
  --linter-exception appstream-screenshot-missing

CLI flags have the highest precedence , so this override applies even if aetherpak.yaml specifies different exceptions.

Example 4: Custom Index Template in Workflow#

Override the landing page template using a workflow input :

jobs:
  publish:
    uses: aetherpak/actions/.github/workflows/publish.yml@v3
    with:
      config: aetherpak.yaml
      index-template: .github/workflows/templates/custom-index.html

The workflow input is translated to AETHERPAK_INDEX_TEMPLATE, which overrides the branding.index_template setting in the config file.

How Overrides Are Applied#

The override mechanism works differently depending on the configuration source :

  1. At startup, Viper loads environment variables with the AETHERPAK_ prefix
  2. During command initialization, the bindFlags() function automatically populates unset CLI flags from Viper (which includes both env vars and config file values)
  3. Flags explicitly set on the command line are marked as "changed" and skip the Viper population step, ensuring CLI flags always win
  4. Per-command logic checks cmd.Flags().Changed("flag-name") to determine whether a flag was explicitly set and should override config/defaults

This design ensures a clear and predictable precedence order while allowing flexible per-environment and per-command customization.

Tip: Use aetherpak --verbose to see which config file is loaded and which values are applied. Debug output includes lines like Using config file: aetherpak.yaml and shows the final resolved configuration.

Tip: For reproducible CI builds, prefer setting values in aetherpak.yaml rather than environment variables. Use env vars and CLI flags for overrides specific to a deployment environment or debugging scenario.

12. Minimal Example#

The following is the smallest valid aetherpak.yaml for a single manifest-based app. Every line is required or strongly recommended for a working deployment .

# aetherpak.yaml — minimal single-app configuration

# Where to push OCI application layers (host + path)
registry: ghcr.io
oci_repository: myorg/myrepo # typically matches your GitHub repo slug

# Where the index and landing page are publicly served
pages_url: https://myorg.github.io/myrepo

# One app to build and publish
apps:
  - id: org.example.MyApp # Reverse-DNS Flatpak application ID
    manifest: org.example.MyApp.json # Path to your Flatpak manifest (relative to repo root)

This configuration:

  • Builds org.example.MyApp for x86_64 (the default architecture)
  • Publishes it to the stable Flatpak branch (the default)
  • Stores layers in ghcr.io/myorg/myrepo
  • Generates a landing page and .flatpakrepo served from https://myorg.github.io/myrepo

Note: pages_url and oci_repository can be omitted if they are passed as workflow inputs (pages-url: and oci-repository:) or environment variables (AETHERPAK_PAGES_URL, AETHERPAK_OCI_REPOSITORY). Declaring them in the file is recommended for consistency.

13. Full Example#

This comprehensive example demonstrates every feature of aetherpak.yaml, including global defaults, branding, linting, channel mappings, and multiple apps with different configurations.

# aetherpak.yaml - Full configuration example demonstrating all features

# ──────────────────────────────────────────────────────────────────────────────
# Global Settings
# ──────────────────────────────────────────────────────────────────────────────

# The OCI registry host where images will be pushed (default: "ghcr.io")
registry: ghcr.io

# The public URL where your repository landing page and index files are hosted
# (required for full publish cycle)
pages_url: https://flatpak.example.com

# The OCI repository path where images are stored (defaults to GITHUB_REPOSITORY)
# Format: owner/repo
oci_repository: example-org/flatpak-apps

# The Flatpak remote name shown to users (also the .flatpakrepo filename)
# Defaults to "<owner>-<repo>" format
remote_name: example-apps

# Human-readable repository title shown on the landing page
# Default: "Flatpak Repository"
repo_title: "Example Organization App Repository"

# URL shown as the repository homepage in metadata (optional)
repo_homepage: https://example.org

# Fallback .flatpakrepo URL for runtime dependency resolution
# Default: Flathub URL (https://dl.flathub.org/repo/flathub.flatpakrepo)
runtime_repo: https://dl.flathub.org/repo/flathub.flatpakrepo

# Disable GPG signing entirely (default: false)
# Set to true to skip signing; end-users will install with --no-gpg-verify
no_sign: false

# Base directory for all output artifacts (optional)
# When set, all output paths default to subdirectories under this path
output_dir: _output

# ──────────────────────────────────────────────────────────────────────────────
# Channel Mappings
# ──────────────────────────────────────────────────────────────────────────────

# Map git references (branches/tags) to Flatpak branch names
# Supports glob patterns (filepath.Match syntax)
# Resolution order: exact match first, then glob patterns (longest pattern wins)
channel_mappings:
  main: beta # Push to 'main' branch → 'beta' channel
  "release/*": stable # All release/* branches → 'stable' channel
  "v*": stable # Version tags (v1.0, v2.1.0, etc.) → 'stable' channel
  "staging/*": alpha # Staging branches → 'alpha' channel

# ──────────────────────────────────────────────────────────────────────────────
# Defaults Section
# ──────────────────────────────────────────────────────────────────────────────

# Global build defaults inherited by all apps (unless overridden per-app)
defaults:
  # Enable compiler cache (default: nil/disabled)
  ccache: true

  # Directory to store ccache artifacts (default: ".ccache")
  ccache_dir: .build-cache/ccache

  # Directory for flatpak-builder state (default: ".state")
  state_dir: .build-cache/state

  # Run linter checks for all apps by default (default: false)
  run_linter: true

  # Additional flatpak-builder command-line arguments
  # Applied to all apps; individual apps can override by providing their own non-empty list
  # Note: As of v0.21.0, --install-deps-from flags are auto-injected for remotes configured
  # in the remotes section, so you typically don't need to specify them here manually
  builder_args:
    - --sandbox
    - --disable-rofiles-fuse

  # Additional Flatpak remotes to register before building
  # Merged with per-app remotes (app values win on conflicts)
  remotes:
    flathub: https://dl.flathub.org/repo/flathub.flatpakrepo
    gnome-nightly: https://nightly.gnome.org/gnome-nightly.flatpakrepo

  # Flatpak runtimes/SDKs to pre-install before building
  # Prepended to per-app flatpaks; duplicates are automatically deduplicated
  flatpaks:
    - remote: flathub
      ref: org.freedesktop.Platform//23.08
    - remote: flathub
      ref: org.freedesktop.Sdk//23.08

# ──────────────────────────────────────────────────────────────────────────────
# Branding Section
# ──────────────────────────────────────────────────────────────────────────────

# Customize the look and feel of the generated landing page
branding:
  # Full URL to your project logo (displayed in the landing page header)
  logo_url: "https://example.org/assets/logo.png"

  # Full URL to your favicon (browser tab icon)
  favicon_url: "https://example.org/assets/favicon.ico"

  # Primary accent color for the landing page (CSS hex color)
  # Default: "#8b5cf6"
  accent_color: "#3584e4"

  # Footer text displayed at the bottom of the landing page
  # Supports HTML markup
  footer_text: "© 2025 Example Organization | Powered by AetherPak"

  # Path to a custom Jinja2/Go HTML template for the index page
  # Replaces the default landing page entirely
  # See documentation for template context structure and helper functions
  index_template: templates/custom-index.html

# ──────────────────────────────────────────────────────────────────────────────
# Linter Section
# ──────────────────────────────────────────────────────────────────────────────

# Global linter configuration (can be overridden per-app)
linter:
  # Treat linter warnings/errors as fatal (default: true)
  # Set to false to report issues without failing the build
  strict: false

  # List of flatpak-builder-lint rule IDs to bypass globally
  ignore_rules:
    - appstream-screenshot-missing
    - appstream-license-missing

  # Alternative/alias for ignore_rules (both lists are merged)
  # Can be overridden via AETHERPAK_LINTER_EXCEPTIONS or --linter-exception flag
  exceptions:
    - desktop-file-icon-key-absent

  # Path to a JSON file containing app-specific linter exceptions
  # Format: {"org.example.App": ["rule1"], "*": ["rule2"]}
  # Can be overridden via AETHERPAK_LINTER_EXCEPTIONS_FILE env var
  exceptions_file: linter-exceptions.json

# ──────────────────────────────────────────────────────────────────────────────
# Apps
# ──────────────────────────────────────────────────────────────────────────────

apps:
  # ─────────────────────────────────────────────────────────────────────────
  # App 1: Manifest-based, multi-arch, with per-app overrides
  # ─────────────────────────────────────────────────────────────────────────
  - id: org.example.AppOne
    # Relative path to the Flatpak manifest file (mutually exclusive with bundles)
    manifest: apps/org.example.AppOne/org.example.AppOne.json

    # Target architectures to build
    # Default for manifest apps: ["x86_64"]
    arches:
      - x86_64
      - aarch64

    # Flatpak branch/channel (default: "stable")
    branch: stable

    # Override global run_linter setting for this app
    # Supports both run_linter and run-linter forms (kebab-case is normalized)
    run-linter: true

    # Per-app linter configuration
    # Additive to global linter config (rules are merged, not replaced)
    linter:
      strict: true
      ignore_rules:
        - appstream-summary-too-long
      exceptions:
        - finish-args-arbitrary-dbus-access

    # Enable ccache for this app (overrides defaults.ccache)
    ccache: true

    # Custom ccache directory for this app
    ccache_dir: .build-cache/ccache-appone

    # Custom state directory for this app
    state_dir: .build-cache/state-appone

    # Per-app builder arguments
    # If non-empty, replaces defaults.builder_args (does not merge)
    builder_args:
      - --sandbox
      - --ccache

    # Per-app remotes (merged with defaults.remotes; app values win on conflicts)
    remotes:
      kde-runtime: https://distribute.kde.org/kderuntime.flatpakrepo

    # Per-app flatpaks (prepended to defaults.flatpaks; duplicates are deduplicated)
    flatpaks:
      - remote: kde-runtime
        ref: org.kde.Platform//6.5

  # ─────────────────────────────────────────────────────────────────────────
  # App 2: Manifest-based, minimal (inherits most defaults)
  # ─────────────────────────────────────────────────────────────────────────
  - id: com.example.AppTwo
    # Minimal configuration: inherits all defaults
    manifest: apps/com.example.AppTwo/com.example.AppTwo.yaml

    # Single architecture (default: ["x86_64"])
    # Explicitly declared here for clarity
    arches:
      - x86_64

    # Uses default branch (stable), default run_linter (true from defaults),
    # default builder_args, remotes, and flatpaks

  # ─────────────────────────────────────────────────────────────────────────
  # App 3: Bundle-based, multi-arch, different branch
  # ─────────────────────────────────────────────────────────────────────────
  - id: net.example.ThirdPartyApp
    # Bundles field: map of arch → {url, sha256} for prebuilt .flatpak files
    # Mutually exclusive with manifest (exactly one required)
    bundles:
      x86_64:
        # URL must start with http:// or https://
        url: https://releases.example.net/ThirdPartyApp_x86_64.flatpak
        # SHA256 must be exactly 64 lowercase hex characters
        sha256: 2159fc643175dcf54f8b9293f48fb8b11c41a87fe31684ee4c5d4e94c6f37a42

      aarch64:
        url: https://releases.example.net/ThirdPartyApp_aarch64.flatpak
        sha256: a7b3c9d2e1f04586738c9d2b4f1e3a6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2

    # Bundles are fetched, verified, imported, and rebound to this branch
    branch: beta

    # Disable linting for bundle-based apps (linting only applies to manifest builds)
    run_linter: false

    # Note: ccache, builder_args, remotes, and flatpaks are ignored for bundle apps
    # (they only apply to manifest-based builds)

What this example demonstrates#

This configuration showcases:

  • Top-level global settings — registry, pages_url, oci_repository, remote_name, repo_title, repo_homepage, runtime_repo, no_sign, output_dir
  • Channel mappings — glob patterns mapping git refs to Flatpak branches (main → beta, release/* → stable, v* → stable, staging/* → alpha)
  • Defaults section — ccache, ccache_dir, state_dir, run_linter, builder_args, remotes, and flatpaks that are inherited by all apps
  • Branding section — logo_url, favicon_url, accent_color, footer_text, and index_template for customizing the landing page
  • Linter section — strict mode, ignore_rules, exceptions, and exceptions_file for controlling linter behavior
  • Manifest-based app (AppOne) with both arches, per-app linter overrides, custom branch, builder_args, remotes, and flatpaks
  • Minimal manifest app (AppTwo) that inherits most defaults, demonstrating the simplest manifest-based configuration
  • Bundle-based app (ThirdPartyApp) with both architectures, URL + SHA256 checksums, different branch, and linting disabled
  • Both kebab-case and snake_case formsrun-linter and run_linter (both are accepted and normalized)
  • Per-app overrides — showing how individual apps can override ccache, ccache_dir, state_dir, builder_args, remotes, and flatpaks
  • Inheritance and merging behavior — remotes are merged (app wins conflicts), flatpaks are prepended and deduplicated, builder_args are replaced if non-empty