---
title: "Configuration"
description: "Config files through c12, the unstorage cache, and where an MCP server looks for its config."
canonical_url: "https://archives.agntn.dev/guide/configuration"
---
# Configuration

> Config files through c12, the unstorage cache, and where an MCP server looks for its config.

## Config files

Under Node, the library loads configuration through [c12](https://github.com/unjs/c12): `archives.config.ts`, `.archives`, or an `archives` key in `package.json`.

```ts [archives.config.ts]
export default {
  storage: {
    cache: true,
    ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
    prefix: "archives",
  },
  performance: {
    concurrency: 3,
    batchSize: 20,
    timeout: 10_000,
    retries: 1,
  },
};
```

Overrides per environment live under `$development`, `$production` and `$test`. `getConfig()` returns the resolved object, `resetConfig()` drops the cache, `resolveConfig({ cwd })` loads from somewhere else without caching. `concurrency` and `batchSize` must be positive integers; invalid values fail before processing starts.

## Timeouts and cancellation

`timeout` applies to each HTTP attempt, including the response body, even when a caller supplies an `AbortSignal`. Retries start with a fresh deadline; cancelling the caller's signal stops retrying. This isn't a deadline for the whole archive operation: index lookup, playback and retries can each take time.

The library accepts finite timeouts from `0` to `2_147_483_647` milliseconds, rounding positive fractions up. `0` disables the deadline without disabling caller cancellation. Tool calls keep their narrower schema limits. HTTP retries don't cover failures while reading a response body.

Wayback, Common Crawl and Archive.today lift the configured value to 60 seconds. The Wayback and Common Crawl indexes regularly need more than ten, and Archive.today keeps the minute its timemap request always asked for. A `timeout` on the provider, on `createArchive` or on the call still wins, shorter or not.

## Cache

Responses are cached through [unstorage](https://github.com/unjs/unstorage), in memory unless you say otherwise. Any driver works:

```ts
import { configureStorage } from "@agntn/archives";
import fsDriver from "unstorage/drivers/fs";

await configureStorage({
  driver: fsDriver({ base: "./cache" }),
  ttl: 24 * 60 * 60 * 1000, // 1 day
});
```

Keys are `{prefix}:{provider key hash}:{query hash}`, where the provider key includes the options that change a result set, such as a Wayback `collapse` or an Archive-It collection. Two calls that differ only in cosmetic options share an entry. `clearProviderStorage()` empties the cache.

Per call:

```ts
await archive.snapshots("example.com", { cache: false }); // skip the cache
await archive.snapshots("example.com", { ttl: 60_000 }); // keep this one for a minute
```

Listings and bodies are cached separately. A body read is cached against the capture it resolved to, so asking for `timestamp: "2015"` twice reads the archive once.

## Where the config is read from

The library looks for config in `process.cwd()`. The MCP server doesn't. A client starts `archives mcp` in whatever directory it has open. And an `archives.config.ts` from a repo you're only browsing is code you never chose to run. So `archives mcp` pins discovery to the home directory of the account running it.

Embedding `createMcpServer()` in your own host? Pin the root yourself, before the first lookup. `setConfigCwd()` comes from the package root, next to `setConfig()`, not from `/mcp`:

```ts
import { homedir } from "node:os";
import { setConfigCwd } from "@agntn/archives";
import { createMcpServer } from "@agntn/archives/mcp";

setConfigCwd(homedir()); // what `archives mcp` does
const server = createMcpServer();
```

Both entries share one config, so the server reads from there. Skip the call and the first lookup runs whatever `archives.config.ts` sits in the current directory. Great news for whoever wrote it.

## Running on Workers or in a browser?

What would config discovery read there? There's no disk. So it doesn't look. The package picks discovery by export condition, and under `browser` or `workerd` it gets a build with no c12 in it. Vite, Wrangler and Nitro's Cloudflare preset all pick that one. You get the defaults. In a Vite build, c12, jiti and a tar extractor stay out of your bundle.

Want your own values there? Hand them over yourself:

```ts
import { setConfig } from "@agntn/archives";

setConfig({ performance: { timeout: 20_000 } });
```

Call it once at startup. Anything you leave out comes from the defaults, and `$env` layers don't apply, since you're passing final values. Under Node, c12 doesn't even load until something asks for discovery. `resetConfig()` hands control back to discovery. This site does exactly that from a Nitro plugin.

## Perma.cc key

Perma.cc is the one provider that needs a credential. In code, pass `apiKey` to the factory. Over MCP and in the agent extensions, the key comes from `PERMA_CC_API_KEY` or `PERMACC_API_KEY` and is never accepted as a tool argument; it is redacted before the options reach a transcript.


## Sitemap

See the full [sitemap](https://archives.agntn.dev/sitemap.md) for all pages.
