# File Descriptor Limits in Linux: ulimit, Systemd, and EMFILE Fixes

A file descriptor limit caps the number of open files, network sockets, and pipes a Linux process or system can maintain simultaneously. Hitting default boundaries triggers EMFILE errors and halts concurrent agent workers, web servers, and database pools. Resolving these bottlenecks requires configuring ulimit, PAM limits, systemd service overrides, and kernel sysctl values.

Source: https://fast.io/resources/file-descriptor-limit/
Author: [Tom Langridge](https://fast.io/authors/tom-langridge/)
Last reviewed: 2026-10-18

## What Is a File Descriptor Limit in Linux and How Does It Work?

When a Linux process exhausts its allocated file descriptors, system calls like open(), socket(), pipe(), and accept() fail immediately with the EMFILE error code ("Too many open files"). Unlike physical memory exhaustion, which causes memory swapping or invokes the kernel Out-of-Memory (OOM) killer, file descriptor exhaustion leaves the offending process running in a degraded state. The process remains alive in the process table, but it cannot establish incoming network sockets, read configuration files, or write debug logs to disk.

A file descriptor limit is an operating system restriction that caps the maximum number of open files, network sockets, and pipes a single process or the entire system can maintain simultaneously. In Unix-like operating systems, "everything is a file." When a process opens a regular file on an ext4 or XFS filesystem, connects to a PostgreSQL database over a TCP socket, creates a POSIX inter-process pipe, or initializes an epoll event listener, the Linux kernel assigns an unsigned integer known as a file descriptor to track that input and output stream.

### The File Descriptor Architecture

Understanding how Linux tracks file descriptors clarifies why limits exist and where bottlenecks emerge. Inside the kernel, file descriptor resolution spans three distinct layers:

1. **Per-Process File Descriptor Table:** Each running process possesses its own private file descriptor table within its task_struct. The table maps small integer values (0 for standard input, 1 for standard output, 2 for standard error, and ascending integers for newly opened resources) to pointers in the system-wide open file table.
2. **System-Wide Open File Table:** The kernel maintains a global list of open file objects represented by struct file instances. This table tracks the access mode (read, write, append), current file offset, and reference counts across all running processes.
3. **VFS Inode Table:** The file object points to an inode representing the physical file on disk or the underlying network socket buffer in memory.

Because file descriptors consume unswappable kernel memory, Linux enforces limits at both the process level and the kernel level to prevent rogue scripts, runaway connection pools, or fork bombs from starving the operating system.

### The Three-Tier Limit Hierarchy

Linux implements file descriptor governance across three hierarchical tiers:

* **Per-Process Soft Limit (RLIMIT_NOFILE):** The current operational threshold enforced by the kernel for an individual process. Most Linux distributions, including Ubuntu, Debian, CentOS, and Red Hat Enterprise Linux, default to a conservative soft limit of `1,024` file descriptors per process. An unprivileged process can dynamically raise its soft limit up to its designated hard limit using the setrlimit() system call.
* **Per-Process Hard Limit:** The absolute ceiling for the soft limit. Once established, an unprivileged user or process can only lower its hard limit. Raising the hard limit requires root privileges or explicit capability grants (CAP_SYS_RESOURCE).
* **System-Wide Kernel Ceiling (fs.file-max):** A global limit configured in `/proc/sys/fs/file-max` that bounds the total number of open file allocations across all running processes on the host. Modern Linux distributions set fs.file-max dynamically during boot based on available physical memory, often establishing limits between `1,048,576` and `65,536,000` handles on modern servers.

### EMFILE vs ENFILE Error Codes

When an application fails to allocate a file descriptor, the returned error code reveals which tier blocked the operation:

* EMFILE (Error Maximum Files per process, numeric value 24): Indicates that the calling process reached its own per-process limit (RLIMIT_NOFILE). Adjacent processes running under other system users continue operating normally.
* ENFILE (Error Network or System File table overflow, numeric value 23): Indicates that the host operating system exhausted the system-wide limit set in `/proc/sys/fs/file-max`. When an ENFILE error occurs, every process across the entire server fails when attempting to open files or accept network handshakes.

### Application Ceilings and the AI Ingestion Dilemma

File limits do not exist solely at the operating system level; they interact directly with application boundaries and AI agent workflows. Autonomous agents and Retrieval-Augmented Generation (RAG) indexers attempt to batch thousands of documents or extract code trees across local processes. In AI agent workflows, similar ceilings appear at the application boundary: as documented by the [Anthropic Help Center](https://support.claude.com/en/articles/8241126-upload-files-to-claude), Claude chat sessions accept up to 20 files at up to 500 MB per file, while Claude Projects accept files up to 30 MB each with no fixed file-count cap, bounded only by the model's context window.

Reaching context ceilings during ingestion forces developers to look for alternative paths, while opening thousands of source files locally trips Linux process file descriptor boundaries before data ever reaches an API.

## How to Check File Descriptor Limits and Active Usage

Diagnosing file descriptor exhaustion requires inspecting both active limits and real-time consumption across running processes. Because different shells, daemons, and system users run under isolated constraints, querying limits from the correct context is essential.

### Checking Process Limits with ulimit

The standard utility for inspecting and modifying resource limits in an interactive shell session is ulimit. By default, `ulimit -n` inspects the soft file descriptor limit for the current shell:

```bash
ulimit -n
```

To explicitly inspect the soft limit, pass the `-Sn` flag:

```bash
ulimit -Sn
```

To inspect the hard ceiling for the current user, pass the `-Hn` flag:

```bash
ulimit -Hn
```

When the command outputs `1024`, the current shell session is restricted to that default allocation until you raise the ceiling.

### Inspecting Running Processes via /proc

When troubleshooting background daemons or multi-threaded applications, checking ulimit in your personal SSH session can be misleading. System services frequently run with completely different resource boundaries.

To inspect the exact limits assigned to a live process by process ID (PID), query its `/proc` entry:

```bash
cat /proc/<PID>/limits | grep "Max open files"
```

The output displays three columns: the soft limit, the hard limit, and the unit of measurement:

```text
Limit                     Soft Limit           Hard Limit           Units
Max open files            1024                 4096                 files
```

To determine how many file descriptors a running process currently holds open, list the entries in its `/proc/<PID>/fd` virtual directory:

```bash
ls -1 /proc/<PID>/fd | wc -l
```

Comparing the count of entries in `/proc/<PID>/fd` against the soft limit in `/proc/<PID>/limits` reveals whether a process is approaching its exhaustion ceiling.

### Inspecting System-Wide File Allocations

To evaluate overall host capacity, query the virtual files provided by the kernel under `/proc/sys/fs/`:

```bash
cat /proc/sys/fs/file-max
cat /proc/sys/fs/file-nr
```

The file-nr virtual file returns three space-delimited integers:

1. **Allocated File Handles:** The number of file handles the Linux kernel has currently allocated.
2. **Free Allocated Handles:** The number of allocated file handles that have been released and are ready for reuse (in modern Linux 2.6 and newer kernels, this figure is typically 0 because the kernel frees unused structures immediately).
3. **Maximum File Handles:** The maximum number of file handles the kernel will permit system-wide (identical to the value in file-max).

For example, an output of `14240 0 2097152` indicates that 14,240 file handles are currently active out of a global capacity of 2,097,152.

### Identifying Top File Descriptor Consumers

When a server exhibits high file descriptor counts, run this one-line shell pipeline to identify the top ten processes holding the highest descriptor counts:

```bash
for pid in /proc/[0-9]*; do
    p="${pid##*/}"
    count=$(ls -1 "$pid/fd" 2>/dev/null | wc -l)
    if [ "$count" -gt 0 ]; then
        echo "$count $p $(cat "$pid/comm" 2>/dev/null)"
    fi
done | sort -rn | head -n 10
```

Once you identify the offending PID, inspect the specific resources it holds using lsof:

```bash
lsof -p <PID>
```

This reveals whether the process is legitimately handling client connections or leaking file handles due to unclosed input and output streams.

### Diagnostic Command Reference

The following comparison table summarizes essential inspection commands, their operational scope, and output interpretations:

| Diagnostic Command | Inspection Scope | Target Interface | Interpretation of Results |
| :--- | :--- | :--- | :--- |
| `ulimit -Sn` | Current shell session | `getrlimit(RLIMIT_NOFILE)` | Shows current soft limit; default is often `1,024`. |
| `ulimit -Hn` | Current shell session | `getrlimit(RLIMIT_NOFILE)` | Shows absolute ceiling an unprivileged user can set. |
| `cat /proc/<PID>/limits` | Single running process | Kernel task_struct | Displays live operational limits for any running daemon. |
| `ls -1 /proc/<PID>/fd \| wc -l` | Single running process | Kernel file descriptor table | Counts currently open sockets, pipes, and files. |
| `cat /proc/sys/fs/file-nr` | System-wide kernel | Kernel Virtual Filesystem | Shows allocated file handles vs global capacity. |
| `lsof -p <PID>` | Single running process | Open file table resolution | Details file paths, network sockets, and FIFO buffers. |

## Configuring Persistent Limits Across Shells, PAM, and Systemd

Increasing file descriptor limits requires configuring the specific subsystem that governs the target workload. A common pitfall in Linux systems administration is running `ulimit -n 65536` in a terminal and expecting background services to inherit the change.

A temporary `ulimit -n` invocation modifies only the current shell process and its child processes, resetting the moment the shell session terminates. Establishing permanent, reboot-resilient limits requires configuring PAM limits for user sessions, systemd unit directives for services, and sysctl parameters for the kernel.

### Ephemeral Changes for Interactive Shells

To raise the limit for a temporary task, batch script, or immediate debugging session, invoke ulimit followed by the desired integer:

```bash
ulimit -n 65536
```

An unprivileged user can only raise their soft limit up to the value of their hard limit (`ulimit -Hn`). If an unprivileged user attempts to specify a value higher than the hard limit, the shell rejects the command with an error message. The superuser (root) can set both soft and hard limits to any value up to the kernel ceiling defined in fs.nr_open.

### Permanent Limits for User Sessions via PAM

For interactive user logins, SSH sessions, and cron tasks managed by Pluggable Authentication Modules (PAM), persistent limits are configured in `/etc/security/limits.conf` or drop-in files under `/etc/security/limits.d/`.

Add configuration directives using the standard format `<domain> <type> <item> <value>`:

```text
*               soft    nofile          65536
*               hard    nofile          65536
root            soft    nofile          65536
root            hard    nofile          65536
appuser         soft    nofile          1048576
appuser         hard    nofile          1048576
```

Two critical details must be observed when editing limits.conf:

1. **The Wildcard Does Not Cover Root:** The asterisk wildcard matches all standard user accounts, but PAM explicitly excludes the root account from wildcard expansion. Always declare separate entries for root.
2. **PAM Module Activation:** Verify that PAM sessions load the pam_limits.so library. In Debian and Ubuntu systems, verify that `/etc/pam.d/common-session` and `/etc/pam.d/common-session-noninteractive` contain:

```text
session required pam_limits.so
```

### Overriding Limits for Systemd Services

The most frequent configuration gap in modern Linux administration involves system services. Systemd does not use PAM for services it spawns. Consequently, background services like Nginx, Docker, PostgreSQL, Redis, and custom Python or Node.js workers completely ignore `/etc/security/limits.conf`.

Modern systemd releases default DefaultLimitNOFILE to `524288:1048576` (`524,288` soft and `1,048,576` hard), but older releases or customized distributions can constrain services to `1,024` descriptors.

To adjust the file descriptor limit for any systemd service, set the property directly using systemctl:

```bash
sudo systemctl set-property custom-worker.service LimitNOFILE=65536
```

Alternatively, create a drop-in configuration file directly on disk:

```bash
sudo mkdir -p /etc/systemd/system/custom-worker.service.d
printf '[%%s]
LimitNOFILE=65536
' "Service" | sudo tee /etc/systemd/system/custom-worker.service.d/override.conf
```

You can specify a single integer to set soft and hard limits equally, or specify two colon-delimited values (`LimitNOFILE=65536:524288`) to establish separate soft and hard boundaries. To grant a high-throughput network proxy or database the maximum allowable descriptors, use `LimitNOFILE=infinity`, which sets the limit to the value defined in fs.nr_open.

Apply the override and restart the service:

```bash
sudo systemctl daemon-reload
sudo systemctl restart custom-worker.service
cat /proc/$(pgrep -o custom-worker)/limits | grep "Max open files"
```

To adjust the default file descriptor limit for all systemd services globally, edit `/etc/systemd/system.conf` and declare:

```ini
DefaultLimitNOFILE=65536:524288
```

### Raising Kernel Boundaries with sysctl

The Linux kernel enforces two global parameters under `/proc/sys/fs/`:

* `fs.file-max`: The system-wide ceiling on open file handles.
* `fs.nr_open`: The maximum value any individual process can assign to its soft or hard RLIMIT_NOFILE. If you configure `LimitNOFILE=2000000` but fs.nr_open is set to `1048576`, systemd or the shell will reject the allocation.

To permanently increase both values across system reboots, create a sysctl configuration file at `/etc/sysctl.d/99-file-limits.conf`:

```ini
fs.file-max = 2097152
fs.nr_open = 2097152
```

Apply the changes immediately without restarting the host:

```bash
sudo sysctl --system
```

### Configuration Matrix Across Subsystems

The following comparison table highlights which configuration file governs each operational environment:

| Configuration File | Governed Subsystem | Target Workload | Persistence Mechanism |
| :--- | :--- | :--- | :--- |
| `ulimit -n <value>` | Shell process table | Interactive shell and child tasks | Ephemeral; lost upon shell exit. |
| `/etc/security/limits.conf` | PAM (pam_limits.so) | Interactive SSH, user logins, cron | Persistent across reboots for user sessions. |
| `/etc/systemd/system/<svc>.d/override.conf` | Systemd service manager | Specific system daemon or service | Persistent; applied via daemon-reload. |
| `/etc/systemd/system.conf` | Systemd core daemon | All systemd services globally | Persistent; requires reboot or daemon reexec. |
| `/etc/sysctl.d/99-file-limits.conf` | Linux virtual filesystem kernel | System-wide allocations and nr_open | Persistent across reboots via sysctl --system. |

## Diagnosing and Resolving EMFILE Errors in Runtimes and Containers

While system administrators often resolve EMFILE errors by raising configuration ceilings, software developers frequently encounter EMFILE due to application architecture defects. In runtime environments like Node.js and Python, EMFILE errors are frequently a symptom of unbounded concurrency or resource leaks rather than insufficient operating system capacity.

### Node.js and JavaScript Runtime Failures

In Node.js applications, file operations execute asynchronously. A common antipattern involves reading a large directory tree by launching unthrottled asynchronous promises:

```javascript
const fs = require('fs/promises');

async function processDirectory(fileList) {
  return Promise.all(fileList.map(filePath => fs.readFile(filePath, 'utf-8')));
}
```

If fileList contains thousands of files and the default soft limit is `1,024`, the Node.js event loop initiates file opening operations until the process exhausts its descriptors, failing with `Error: EMFILE: too many open files`.

To fix this issue without raising system limits, throttle concurrent operations using batching:

```javascript
const fs = require('fs/promises');

async function processWithBatching(fileList, concurrency = 64) {
  const results = [];
  for (let i = 0; i < fileList.length; i += concurrency) {
    const chunk = fileList.slice(i, i + concurrency);
    const chunkResults = await Promise.all(
      chunk.map(file => fs.readFile(file, 'utf-8'))
    );
    results.push(...chunkResults);
  }
  return results;
}
```

Similarly, tools that watch filesystem changes (such as Chokidar or Webpack file watchers) allocate an inotify file descriptor per watched directory. On massive repositories, raising `/proc/sys/fs/inotify/max_user_watches` alongside LimitNOFILE prevents watcher crashes.

### Python Asyncio and Multiprocessing Leaks

In Python, an OSError with errno 24 ("Too many open files") commonly occurs when workers open files or HTTP client sessions without proper context managers:

```python
def load_corpus(paths):
    records = []
    for path in paths:
        f = open(path, 'r')
        records.append(f.read())
    return records
```

If an exception occurs or if Python's garbage collector delays cleanup, file descriptors remain allocated. Always use context managers (`with open(path) as f:`) for deterministic closing of descriptors upon block exit.

In high-concurrency asyncio applications, bound simultaneous network requests using asyncio.Semaphore:

```python
import asyncio
import aiohttp

async def fetch_urls(urls, max_concurrency=100):
    semaphore = asyncio.Semaphore(max_concurrency)
    async with aiohttp.ClientSession() as session:
        async def fetch(url):
            async with semaphore:
                async with session.get(url) as response:
                    return await response.text()
        return await asyncio.gather(*(fetch(u) for u in urls))
```

### Docker and Containerized Agent Limits

Containers run in isolated namespaces, but they do not automatically inherit host PAM configurations. By default, a container inherits its file descriptor boundaries from the container engine daemon.

To set file descriptor limits on an individual Docker container, pass the `--ulimit` flag at runtime:

```bash
docker run -d --name agent-worker --ulimit nofile=65536:65536 python:3.11-slim
```

In Docker Compose configurations, define limits within compose.yaml:

```yaml
services:
  agent-runner:
    image: custom-agent:latest
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
    restart: on-failure
```

To configure global container limits across all Docker workloads on a host, define default ulimits in `/etc/docker/daemon.json`:

```json
{
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 65536,
      "Soft": 65536
    }
  }
}
```

After updating daemon.json, execute `sudo systemctl restart docker` to apply the baseline limits to all newly initialized containers.

## Architectural Strategies: Offloading File I/O to Intelligent Cloud Workspaces

While raising operating system limits and batching local file reads stabilizes single-node services, relying on local filesystem handles presents major architectural limitations when scaling autonomous agents and data extraction pipelines.

When background agents process extensive technical manuals, legal binders, or code repositories, maintaining thousands of open file descriptors on local Linux servers creates memory bloat, high kernel overhead, and file synchronization collisions. Instead of holding thousands of open file handles across ephemeral container disks, modern architectures decouple file storage from compute with cloud workspaces.

### Decoupling File I/O with Fast.io Workspaces

In an intelligent workspace architecture, document collections and project assets reside in org-owned [Fast.io workspaces](/product/workspaces/). Rather than requiring local agent processes to mirror, unpack, and open local files on an ext4 volume, the workspace is the persistent collaboration layer for both human teams and AI agents.

Teams can ingest documents through several native paths:

* **Direct Uploads:** Ingest files directly via the browser or API, using chunked upload mechanics that support large file ingestion up to plan limits (`25 GB` on Starter, `50 GB` on Business, and `100 GB` on Enterprise).
* **Cloud Sync:** Cloud Sync connects Google Drive, Dropbox, Box, and OneDrive folders to a workspace, one-way or two-way, on a schedule or on demand.
* **URL Import:** Ingest files directly from public web addresses and cloud storage links without drawing down local network bandwidth or allocating local server file descriptors.

### Native Intelligence Mode and MCP Tooling

Once documents arrive in a workspace, enabling Intelligence Mode indexes the content automatically across full-text, semantic vectors, and metadata values. This eliminates the need for teams to manage independent vector databases, chunking workers, or local file indexing loops.

Autonomous assistants and coding agents interact with the workspace via the remote Model Context Protocol (MCP) server over Streamable HTTP:

* **General Assistants and Workflow Agents:** Connect Claude Desktop, Claude Cowork, OpenClaw, or custom agents via `https://mcp.fast.io/mcp/tools`.
* **IDE and Coding Agents:** Connect Claude Code, Cursor, VS Code, and terminal agents via `https://mcp.fast.io/mcp/code`.
* **ChatGPT and Codex Custom Servers:** Connect via `https://mcp.fast.io/mcp/operations` (or install the Fast.io plugin directly from the ChatGPT plugin directory).

Using the MCP storage search action, an agent queries documents by meaning and retrieves only the precise citations needed to answer a prompt. This completely bypasses local Linux EMFILE bottlenecks: an agent analyzing a repository of 50,000 files requires only a single HTTP connection to the MCP endpoint rather than 50,000 open local file descriptors.

### Collaborative Capabilities for Agentic Teams

Intelligent cloud workspaces provide capabilities that local Linux filesystems cannot deliver:

* **Metadata Views:** Teams can transform unstructured files into queryable spreadsheets with typed schemas at [/product/document-data-extraction/](/product/document-data-extraction/). AI automatically extracts fields like contract counterparties, renewal dates, and line item totals from PDFs, images, and office documents without manual OCR rules.
* **Collaborative Notes:** Notes kept beside the files for people and agents use version-checked saves to prevent conflicting edits between human collaborators and automated workers.
* **Per-File Version History:** Every modification creates a discrete version, allowing teams to audit changes, compare diffs, and restore prior states across concurrent multi-agent operations.
* **Detailed Activity Log:** An organization-wide activity log captures changes made across workspaces and shares, recording uploads, edits, moves, and deletions without tracking read-only views or downloads.
* **Ownership Transfer:** Autonomous agents can create an organization, configure workspaces and branded shares, and transfer full organization ownership to a human stakeholder while retaining administrative access.

Monthly plans start with a 14-day free trial, which requires a credit card. Teams can scale across Starter (`$9.99/mo` with 3 seats and `250 GB` storage), Business (`$49.99/mo` with 10 seats and `5 TB` storage), and Enterprise (`$199.99/mo` with 30 seats and `25 TB` storage) tiers. Explore persistent workspace storage for agentic teams at [/storage-for-agents/](/storage-for-agents/) and review setup steps at `https://mcp.fast.io/docs`.

## Frequently asked questions

### How do I check the file descriptor limit in Linux?

To check the file descriptor limit for your current shell session, run ulimit -n for the soft limit or ulimit -Hn for the hard limit. To inspect the limits of any running process, run cat /proc/<PID>/limits | grep 'Max open files' substituting the target process ID. To view the system-wide global limit enforced across all processes, run cat /proc/sys/fs/file-max.

### What causes the Too many open files error?

The Too many open files error (EMFILE) occurs when a process attempts to open a new file, network socket, pipe, or event listener after already reaching its allocated per-process file descriptor limit. Common causes include unclosed database connection pools, unmanaged file handle leaks in application code, high concurrent traffic on web servers, or batch processing large directories simultaneously without concurrency throttling.

### How do I permanently increase ulimit open files limit?

To permanently increase the ulimit open files limit for user sessions, add soft and hard nofile entries to /etc/security/limits.conf or a drop-in file in /etc/security/limits.d/. Ensure that /etc/pam.d/common-session includes session required pam_limits.so. For system services, configure LimitNOFILE inside a systemd drop-in override file using systemctl edit.

### What is the difference between soft and hard file descriptor limits?

A soft file descriptor limit is the active threshold enforced by the Linux kernel during standard operations; exceeding it triggers an EMFILE error. A hard file descriptor limit acts as the ceiling for the soft limit. An unprivileged user can dynamically increase their soft limit up to their assigned hard limit, but only the root user can increase the hard limit.

### Why does systemd ignore limits defined in /etc/security/limits.conf?

Systemd does not run user-space services through PAM (Pluggable Authentication Modules) sessions, meaning directives configured in /etc/security/limits.conf are completely bypassed. To set file descriptor limits for a systemd service, you must define the LimitNOFILE directive within the service unit file or create an override using systemctl edit <service_name>.

### What is the difference between EMFILE and ENFILE error codes?

EMFILE (errno 24) indicates that an individual process has exceeded its personal file descriptor limit (RLIMIT_NOFILE), while adjacent processes on the system remain unaffected. ENFILE (errno 23) indicates that the entire operating system has exhausted the global file handle allocation table defined in /proc/sys/fs/file-max, causing all file and socket operations across the server to fail.

### How do I set file descriptor limits inside Docker containers?

To set file descriptor limits on a single Docker container, use the --ulimit nofile=<soft>:<hard> runtime flag, such as docker run --ulimit nofile=65536:65536. In Docker Compose, define ulimits.nofile.soft and ulimits.nofile.hard under the service definition in compose.yaml. To set host-wide container defaults, configure default-ulimits in /etc/docker/daemon.json.

### How can AI agents search large document corpora without exhausting OS file descriptors?

Instead of holding thousands of open file handles on local disks, AI agents can store document collections in cloud workspaces like Fast.io. With Intelligence Mode enabled, documents are indexed for hybrid search upon upload. Assistants connect through a remote Model Context Protocol (MCP) server over Streamable HTTP, querying only the necessary passages and eliminating local file descriptor bottlenecks.

## Sources

- [Claude Help Center: Upload files to Claude](https://support.claude.com/en/articles/8241126-upload-files-to-claude): Claude chat sessions accept up to 20 files at up to 500 MB per file, while Claude Projects accept files up to 30 MB each with no fixed file-count cap, bounded only by the model's context window.

## About Fast.io

Fast.io provides shared workspaces where people and AI agents work on the same files, with built-in semantic search and citation-backed chat over what they hold. Agents reach it through a remote MCP server, a REST API at https://api.fast.io/current/, and a command line client published on npm as @vividengine/fastio-cli. MCP setup is at https://mcp.fast.io/docs: Claude and most MCP clients connect to https://mcp.fast.io/mcp/tools, ChatGPT to https://mcp.fast.io/mcp/operations, and coding agents to https://mcp.fast.io/mcp/code.
