arcature 0.1.0

Arcature: an opinionated full-stack Rust web framework. One package, batteries included.
Documentation
# cargo audit: the RustSec advisory database against the checked-in lockfile,
# on a clock.
#
# ci.yml already runs cargo-deny, which checks the same advisory database over
# the same graph. This is not that check run twice; it is the same check run
# at the time the other one cannot run.
#
# An advisory is published against a crate that has been in Cargo.lock for
# eight months. No commit is coming -- the dependency did not change, the code
# did not change, nothing triggers a pull request. ci.yml's `deny` job only
# fires on push and pull_request, so it will next look at that lockfile
# whenever somebody happens to open a pull request, which could be tomorrow or
# could be in three weeks. The advisory sits undetected for the gap.
#
# That gap is the entire reason this file exists. The daily cron closes it to
# at most 24 hours. The `Cargo.lock` path trigger is the cheap half: a
# dependency bump gets an advisory answer in seconds rather than waiting for
# the full cargo-deny graph resolve in ci.yml.
#
# Division of labour, so neither grows into the other:
#
#   deny.toml / ci.yml   licences, banned crates, registries, yanked crates,
#                        duplicate versions. The policy lives there. Advisory
#                        ignores go in deny.toml's `[advisories] ignore`.
#   this file            advisories, on a schedule, and nothing else.

name: Audit

on:
  push:
    branches: [main]
    paths:
      - "Cargo.lock"
      - "Cargo.toml"
      - "macros/Cargo.toml"
      - ".github/workflows/audit.yml"
  pull_request:
    paths:
      - "Cargo.lock"
      - "Cargo.toml"
      - "macros/Cargo.toml"
      - ".github/workflows/audit.yml"
  schedule:
    # Daily. Minute is not :00, and 03:38 sits clear of ci.yml's 04:17
    # powerset so the two are not competing for runners.
    - cron: "38 3 * * *"
  workflow_dispatch:

# Reading the lockfile is the whole job. A finding surfaces as a failed run,
# which GitHub already mails to watchers -- `rustsec/audit-check` would open
# an issue instead and wants `issues: write` for it, which is a repository
# write granted to a scheduled job for a notification the platform sends for
# free.
permissions:
  contents: read

concurrency:
  group: audit-${{ github.ref }}
  # Never on the scheduled run. A cancelled nightly is a day with no advisory
  # answer, and this workflow exists precisely to guarantee there is one every
  # day. On a branch under review a new push supersedes the old lockfile, so
  # cancelling there is free.
  cancel-in-progress: ${{ github.event_name != 'schedule' }}

jobs:
  audit:
    name: RustSec advisories
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      # `cargo audit` is a cargo subcommand, so it needs a toolchain on PATH
      # even though it compiles nothing -- it parses Cargo.lock.
      - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1
        with:
          toolchain: stable

      # Same installer ci.yml uses for cargo-hack and mdbook. A prebuilt
      # binary, not `cargo install`, which would be a two-minute compile on
      # every one of these runs.
      - uses: taiki-e/install-action@a2a5f6e99e1a31540baa0468acfa302cff0f359f # v2.86.4
        with:
          tool: cargo-audit

      # Plain `cargo audit`: fails on vulnerabilities, warns on unmaintained
      # and unsound. Not `--deny warnings`. An `unmaintained` advisory often
      # has no fixed version to move to -- the crate is unmaintained, that is
      # the finding -- and a daily job that fails red with no available action
      # gets muted within a week, taking the vulnerability signal with it.
      # If a specific unmaintained crate should block, that decision belongs
      # in deny.toml with a reason attached, not as a blanket flag here.
      #
      # RUSTSEC-2026-0235 (rkyv 0.7, out-of-bounds read on a malformed
      # archive) is ignored here and nowhere else. `rust_decimal` declares
      # `rkyv` as an optional dependency and nothing in this workspace turns
      # that feature on, so the crate is never compiled: `cargo tree -i rkyv
      # -e all --target all` finds no path to it, and ci.yml's cargo-deny job
      # -- which resolves features and therefore never sees it -- passes.
      # Cargo.lock records the maximal resolve rather than the built graph,
      # which is why `cargo audit` reports it and cargo-deny does not.
      #
      # This is the one case the division of labour above does not cover: the
      # ignore cannot live in deny.toml, because cargo-deny has nothing to
      # ignore. Drop the flag when `rust_decimal` releases a version whose
      # optional `rkyv` dependency is >=0.8.17, or when the workspace stops
      # depending on `rust_decimal` at all.
      - name: cargo audit
        run: cargo audit --ignore RUSTSEC-2026-0235