--- title: "Quick Start" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Quick Start} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(getaca) # The session temp directory carries the account name of whoever knits, so # printing a cache path would put it in the built article. Redact it. local({ roots <- unique(c(tempdir(), normalizePath(tempdir(), winslash = "\\", mustWork = FALSE), normalizePath(tempdir(), winslash = "/", mustWork = FALSE))) roots <- unique(c(gsub("\\", "\\\\", roots, fixed = TRUE), roots)) roots <- roots[order(nchar(roots), decreasing = TRUE)] render <- knitr::knit_hooks$get("output") knitr::knit_hooks$set(output = function(x, options) { for (root in roots) x <- gsub(root, "", x, fixed = TRUE) render(x, options) }) }) # Everything below runs against a throwaway cache with the network switched # off, so this vignette builds identically on a machine that has never seen # these resources and on one that has them all. .old_options <- options(getaca.cache = file.path(tempdir(), "getaca-quickstart")) .old_envvars <- Sys.getenv(c("GETACA_OFFLINE", "NOT_CRAN"), unset = NA) Sys.setenv(GETACA_OFFLINE = "true", NOT_CRAN = "true") ``` A package that needs a four-gigabyte reference dataset cannot ship it, cannot download it during `R CMD check`, and cannot afford to fetch a different version on Tuesday than it fetched on Monday. `getaca` is the layer that makes those three constraints compatible. There is one engine and many declarations. Packages say what they need; `getaca` gets it. The declaration is a small R object that ships inside the installed package, so retrieving a resource requires no registration call, no load hook, and no service to be up. This vignette walks the whole cycle: declaring a resource, retrieving it, behaving during checks, reading its provenance, and knowing where it went. Each section links the companion article that goes deeper. ## Declaring what you need A resource record names exact bytes: one checksum, one version label, and the places those bytes live. ```{r} backbone <- resource( name = "backbone", version = "2026-06", urls = c("https://primary.invalid/backbone-2026-06.zip", "https://mirror.invalid/backbone-2026-06.zip"), sha256 = strrep("9f", 32), size = 4.1e9, license = "CC-BY-4.0" ) backbone ``` Each field earns its place. The `sha256` is what makes the record a record rather than a bookmark: a truncated transfer, a proxy that served an HTML error page, and a publisher who quietly recut the file all produce a different digest, and all three are caught before the bytes reach your code. The `size` is a cheap early check, so a transfer that ends at 40% fails as a truncation rather than running a four-gigabyte hash to discover the same thing. Extra `urls` are tried in order, which turns one host's outage into a slower first call. The `license` travels into provenance, so a result can say what terms the data came under. Records live in a registry, which is scoped to the declaring package: ```{r} reg <- registry(package = "yourpkg", resources = list(backbone)) reg ``` Scoping matters more than it looks. Identity here is the triple `package / name / version`, so two packages may both declare something called `"backbone"` and never collide, in the cache or anywhere else. ```{r} format(resource_id("yourpkg", "backbone", "2026-06")) ``` Ship the registry where `getaca` looks for it, and there is nothing else to wire up: ```{r, eval = FALSE} registry_write(reg, "inst/getaca/registry.rds") ``` Discovery is a file test across the library paths. `getaca` asks `system.file()` for `getaca/registry.rds` inside each installed package, which is why a declaring package needs no `.onLoad()` and no dependency on `getaca` being attached. See `vignette("declaring")` for the full authoring guide: mirrors, derived artefacts, YAML authoring and the pre-ship checklist. ## Getting it ```{r, eval = FALSE} path <- getaca("backbone", package = "yourpkg") ``` That path points to a complete file, verified against the declared checksum, at the resolved version, in a cache slot `getaca` owns. What happens on the way there depends on what is already true: - cached and intact, the path comes back after a size check - cached but past the re-verification interval, the bytes are re-hashed first - absent, the mirrors are tried in order into a temporary file, which is sized and hashed before it is moved into place An interrupted transfer resumes rather than restarting, and can never be mistaken for a finished resource. The temporary file is named after the declared checksum and the mirror that produced it, so a resumed transfer only ever continues bytes from the same host. Sharing one partial file across mirrors would let a failed attempt at the first be resumed onto by the second, and the resulting corruption is indistinguishable from the publisher having changed the file. Two sessions asking for the same resource at the same time take a per-resource directory lock. The second waits, then observes the first session's success and returns the same path, so a four-gigabyte download happens once. `vignette("cache")` covers the locking protocol and what happens to a lock whose holder died. ## Which version a bare name resolves to `getaca("backbone")` asks for a name, and the registry says which record that name means. When a package offers several versions, it states the answer: ```{r} multi <- registry( package = "yourpkg", current = c(backbone = "2026-09"), resources = list( resource("backbone", "2026-06", urls = "https://primary.invalid/backbone-2026-06.zip", sha256 = strrep("9f", 32), license = "CC-BY-4.0"), resource("backbone", "2026-09", urls = "https://primary.invalid/backbone-2026-09.zip", sha256 = strrep("ab", 32), license = "CC-BY-4.0") ) ) resolve_resource("backbone", registry = multi)$id ``` The older record stays resolvable by asking for it: ```{r} resolve_resource("backbone", registry = multi, version = "2026-06")$id ``` Version strings here are labels rather than semantic versions. `source-2026-06_build-3` is a perfectly good label and has no defensible ordering, so `getaca` never tries to rank them. A registry that declares two versions of a name and states no head is refused: ```{r, error = TRUE} registry( package = "yourpkg", resources = list( resource("backbone", "2026-09", urls = "https://primary.invalid/a", sha256 = strrep("ab", 32)), resource("backbone", "2026-03", urls = "https://primary.invalid/b", sha256 = strrep("cd", 32)) ) ) ``` The error lands on the author's machine, at the moment the registry is built, rather than on every user of the package. ## Surviving R CMD check Resolution collapses to `offline` under check, whatever policy is set, so a check run never reaches the network. Three helpers cover the three places that matters, and they answer three different questions. In tests, skip cleanly and say what is missing: ```{r, eval = FALSE} test_that("the backbone parses", { getaca_skip_if_unavailable("backbone", package = "yourpkg") expect_s3_class(read_backbone(getaca("backbone", package = "yourpkg")), "backbone") }) ``` In examples and vignettes, degrade to a message rather than an error. This vignette is running with the network switched off, so the call below takes exactly the path a CRAN check machine would take: ```{r} path <- getaca_optional("backbone", registry = reg) is.null(path) ``` And where a plain logical reads better: ```{r} getaca_available("backbone", registry = reg) ``` `getaca_available()` never touches the network. It asks whether a cached copy exists and passes its cheap integrity check, which is what makes it safe to call in a condition that gates expensive work. To prepare a machine that will later be offline, or a CI job that should find everything already present: ```{r, eval = FALSE} getaca_prefetch("backbone", package = "yourpkg") getaca_prefetch(package = "yourpkg") # everything the package declares ``` Setting `GETACA_CACHE` points any session at a cache that has already been seeded, which is how a CI job restores a cached directory and finds the resources waiting. `vignette("checks")` has the workflow files. ## When it does not work Every failure is classed, and carries an `actor` field naming who can act on it. Here is the one a check run produces, in full: ```{r} err <- tryCatch(getaca("backbone", registry = reg), getaca_error = function(e) e) class(err) ``` ```{r} cat(conditionMessage(err)) ``` ```{r} err$actor ``` `getaca_error_offline` is a subclass of `getaca_error_unavailable`, so a handler for the general case catches it and a narrower handler can separate the two. A declaring package usually catches the ones its users will meet and answers in its own vocabulary: ```{r, eval = FALSE} install_backbone <- function(name = "backbone") { path <- tryCatch( getaca(name, package = "yourpkg"), getaca_error_unavailable = function(e) { stop("The backbone is not installed and no network is available.\n", "Connect, then run: yourpkg::install_backbone(\"backbone\")", call. = FALSE) } ) open_backbone(path) } ``` Six conditions cover the failure surface, and they distinguish causes a plain downloader reports identically. `vignette("failures")` works through each one, including the case where several independent mirrors agree with each other and disagree with the registry. ## Knowing where a file came from ```{r, eval = FALSE} getaca_info("backbone", package = "yourpkg") #> yourpkg/backbone@2026-06 #> path ~/.cache/R/getaca/yourpkg/backbone/2026-06/raw/backbone-2026-06.zip #> sha256 9f9f9f... #> size 4,100,000,000 bytes #> license CC-BY-4.0 #> resolved by bundled registry sha256:1c4d7a90f2be (published 2026-07-20) #> source url https://primary.invalid/backbone-2026-06.zip #> getaca 0.0.0.9000 #> fetched 2026-07-26 11:02:13 #> verified 2026-07-26 11:09:44 (full re-hash) #> checked 2026-07-26 15:31:02 (size and mtime) ``` The registry digest names the exact declaration that chose these bytes, so "which version of yourpkg's declaration was this" has an answer years later, without anyone having kept a revision number in step by hand. Four timestamps, kept apart on purpose. "Verified" means the bytes were re-hashed then. "Checked" means size and modification time were compared against the entry, which is the cheap test run on ordinary access. "Accessed" is the clock the retention sweeps read. Collapsing them into one "last checked" field would make a resource verified in January look verified today because someone opened it this morning. An uncached resource gives `NULL` from `getaca_info()`, which keeps the call usable in a report covering a machine that holds some of the set: ```{r} is.null(getaca_info("backbone", registry = reg)) ``` `getaca_catalogue()` widens that to a data frame covering both halves: every resource the installed packages declare, and every copy the cache holds. ```{r} getaca_catalogue(registry = multi)[, c("name", "version", "current", "declared", "cached")] ``` The three logical columns answer three different questions. `current` marks the version a bare request resolves to. `declared` says whether the registry in force names that version at all. `cached` says whether a local copy is recorded. A row with `declared = TRUE, cached = FALSE` is work still to do on this machine. A row with `declared = FALSE` is a copy of a version nothing asks for any more, which is what the retention sweeps reclaim first. ## Where things are stored ```{r} getaca_cache_dir() ``` That is the temporary directory this vignette is sandboxed in. The default is `tools::R_user_dir("getaca", "cache")`, which is what CRAN policy permits, on the condition that contents are actively managed. `getaca` treats that as a retention policy rather than a function users might find, and sweeps after every successful retrieval. The `getaca.cache` option and the `GETACA_CACHE` environment variable override it, in that order. The layout is scoped the same way identity is: ``` / .locks/ per-resource locks .tmp/ in-flight downloads, never visible as cache / index.rds provenance for this package only // raw/ verified bytes as served proc-/ processed result, own provenance ``` Nothing about it is private. It is an ordinary directory tree, which is what makes `GETACA_CACHE` and a CI cache key sufficient for seeding. To see what a sweep would remove without removing it: ```{r} getaca_clean(dry_run = TRUE) ``` An empty result on a fresh cache. On a working one, each row names the resource, the reason, and the bytes it would reclaim. `vignette("cache")` covers the four sweeps, the clocks they read, and what is never touched. ## Choosing a policy The registry declares a default, and a session or a single call can override it: ```{r} getaca_policy() ``` ```{r, eval = FALSE} getaca_policy("current") # for this session getaca("backbone", package = "yourpkg", policy = "bundled") # for this call ``` `bundled` is the default because a dependency that resolves differently on different days is not a dependency. `current` consults an author-controlled remote registry, which lets a dead mirror be repaired without a CRAN release. `pinned` resolves through a frozen local snapshot, so an analysis keeps resolving what it was written against. `offline` never reaches for the network at all. ```{r, eval = FALSE} getaca_pin(c("yourpkg", "otherpkg")) # writes getaca.pins.rds in the project ``` `vignette("policies")` covers what a remote channel is allowed to change, why redefining a published version is refused, and how pinning interacts with `renv`. ## Where to go next - `vignette("declaring")` for package authors: what to declare, where to put it, and the checklist before shipping - `vignette("policies")` for channels, remote registries and pinning - `vignette("checks")` for `R CMD check`, CI workflows and seeding a cache - `vignette("cache")` for the layout, verification schedule, locking and retention - `vignette("failures")` for the eleven conditions and how to handle each - `vignette("alternatives")` for choosing between `getaca`, a companion data package, and the neighbouring tools ```{r, include = FALSE} options(.old_options) Sys.unsetenv(names(.old_envvars)[is.na(.old_envvars)]) .restore <- .old_envvars[!is.na(.old_envvars)] if (length(.restore)) do.call(Sys.setenv, as.list(.restore)) ```