Skip to main content
Glama
sammkoo

Synology NAS Connector

by sammkoo
README.md
# Synology NAS Connector for ChatGPT

Independent, MIT-licensed **v0.1 developer preview**. A read-only MCP server for selected NAS folders, a portable Node.js core, a DSM dashboard, and a reproducible DSM `.spk` builder. No OpenAI API key, DSM password, file upload, outbound relay, telemetry or inference calls are required.

**Implemented:** folder listing, filename search, metadata, bounded UTF-8 document reads; MCP stdio and stateless Streamable HTTP; bearer-token development authentication; DSM package lifecycle. Build 0002 adds an authenticated management bridge, graphical share selection and live policy revocation, pending real DSM CGI validation.

**Planned:** production MCP OAuth, account pairing, Sign in with ChatGPT, public plugin distribution and an optional outbound relay. These features are visibly disabled. A `.spk` build is not proof of installation compatibility; real DSM testing remains a release gate.

## Quick start

Use Node.js 22 (Node.js 24 is also covered by CI) and Python 3 for packaging.

```sh
npm ci
npm run init
```

Edit `.local/config.json`; new configurations have **no roots**. Add only specific folders:

```json
"roots": [{"id": "documents", "label": "Documents", "path": "/absolute/path/to/selected/folder"}]
```

Keep the token file private, owned by the service user, with mode `0600`. The generated configuration binds to `127.0.0.1` and permits only the two local dashboard origins.

```sh
npm run check
npm run build
node dist/server.cjs --config .local/config.json
```

Open `http://127.0.0.1:8787`. Enter the locally generated token from `.local/token` to see service status and allowed folder aliases. Copy it locally; never commit it. The dashboard stores no credentials. Root access is configured by editing the private config and restarting, rather than granting file permissions from a browser.

## Local MCP clients

Use an absolute server and config path in a client that supports stdio:

```json
{
  "mcpServers": {
    "synology-nas": {
      "command": "node",
      "args": ["/absolute/path/to/dist/server.cjs", "--stdio", "--config", "/absolute/path/to/.local/config.json"]
    }
  }
}
```

Stdio trusts the local spawning process and OS identity; it does not require a bearer token. It uses the same root policy and core as HTTP and DSM. This configuration example is for clients that accept this format; it is not a claimed ChatGPT desktop configuration.

HTTP clients use `POST /mcp` with `Authorization: Bearer <local-token>` and MCP transport headers. There is no legacy SSE transport. Static tokens are for local/self-hosted development, not a completed public ChatGPT authentication flow.

| Tool | Inputs | Result |
| --- | --- | --- |
| `list_roots` | none | allowed root IDs and labels |
| `list_directory` | `rootId`, relative `path`, `limit`, `offset` | entries, `nextOffset`, partial-coverage indicators |
| `search_files` | `rootId`, filename `query`, `limit` | matching files, partial-coverage indicators |
| `get_metadata` | `rootId`, relative `path` | type, bytes, modification time |
| `read_text` | `rootId`, relative `path`, `startLine`, `maxLines` | text, line range, trust marker |

Text support: `.txt`, `.md`, `.csv`, `.tsv`, `.json`, `.xml`, `.yaml`, `.yml`, `.log`, `.rst`, strictly UTF-8. PDF, DOCX, spreadsheets, OCR and archives are outside v0.1. Search matches filenames only and does not index file contents. Default directory scans stop at 5,000 examined entries; tool output is capped at 200 entries. Use `nextOffset` for another directory page. `scanTruncated` means the scan budget stopped discovery; further pages cannot recover unscanned entries. Pagination is not a snapshot if the directory changes. There is no persistent search index.

## Docker

After generating `.local`, set `http.host` to `0.0.0.0` **inside the container config**, and use `/data/documents` as the root path. Keep `tokenFile` as `token`, allowed hosts as `127.0.0.1`/`localhost`, and the local dashboard origins. Compose publishes only on the host loopback interface and mounts both config and documents read-only.

```sh
NAS_UID=$(id -u) NAS_GID=$(id -g) docker compose up --build
```

The selected UID/GID must own `.local/token` and be able to traverse the mounted directories. Linux Docker uses descriptor-relative file access. The image runs without root, capabilities or a writable root filesystem. No Docker daemon is required for native local development.

## DSM package

```sh
npm run build
npm run spk
npm run test:spk
```

Output: `artifacts/SynologyNASConnector-0.1.0-0002-noarch.spk` and its SHA-256 checksum. See [DSM management preview](docs/dsm-management.md) for the new setup flow and device-validation limits. The [original installation guide](docs/installation.md) also documents manual/local diagnostics.

## Project layout

```text
packages/core/      filesystem policy, limits, config; no DSM or MCP dependency
packages/auth/      local-token authentication and future OAuth adapter contract
packages/management/ signed bridge, share catalog and private configuration service
apps/server/        MCP tool definitions, HTTP transport, stdio CLI
apps/dsm-ui/        static dashboard served locally and packaged for DSM
apps/dsm-bridge/    authenticated DSM CGI bridge; no DSM credentials forwarded
packaging/synology/ INFO, privilege policy, lifecycle scripts and DSM launcher
scripts/           portable bundle, SPK builder, package and Docker verification
tests/             policy, authentication and real MCP SDK client tests
docs/              architecture, installation, integration evidence, threat model
```

GitHub Actions tests on Linux with Node.js 22/24, builds and verifies `.spk`, uploads package artifacts, audits production dependencies, and builds/smoke-tests Docker. Source is published at [sammkoo/synology-nas-connector](https://github.com/sammkoo/synology-nas-connector). No release is published automatically; production promotion requires the [product acceptance evidence](docs/product-plan.md).

See [architecture](docs/architecture.md), [verified OpenAI integration points](docs/openai-integration.md), [security](SECURITY.md), [verification record](docs/verification.md), and [contributing](CONTRIBUTING.md).