intranet

TotalClaw 作者 totalclaw v3.2.7

具有插件支持的轻量级本地 HTTP 文件服务器。从 webroot 提供静态文件,通过配置在 URL 前缀安装插件目录,并作为 CGI 运行 index.py 入口点。

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install totalclaw:totalclaw~odrobnik-intranet
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~odrobnik-intranet/file -o odrobnik-intranet.md
# Intranet

Lightweight local HTTP file server — no Apache/nginx needed, no root required. Serves static files, mounts plugin directories, and runs `index.py` entry points as CGI.

**Entry point:** `{baseDir}/scripts/intranet.py`

## Setup

See [SETUP.md](SETUP.md) for prerequisites and setup instructions.

## Commands

```bash
python3 {baseDir}/scripts/intranet.py start                          # Start on default port 8080
python3 {baseDir}/scripts/intranet.py start --port 9000              # Custom port
python3 {baseDir}/scripts/intranet.py start --host 0.0.0.0            # LAN access (requires token + allowed_hosts)
python3 {baseDir}/scripts/intranet.py start --token SECRET            # Enable bearer token auth
python3 {baseDir}/scripts/intranet.py status                         # Check if running
python3 {baseDir}/scripts/intranet.py stop                           # Stop server
```

## Directory Layout

```
{workspace}/intranet/
├── config.json          # Server config (NOT served)
└── www/                 # Webroot (served files go here)
    ├── index.html
    └── ...
```

Config lives in `{workspace}/intranet/config.json`, webroot is `{workspace}/intranet/www/`. The config file is never exposed to HTTP.

## Plugins

Plugins mount external directories at URL prefixes. Configure in `config.json`:

```json
{
  "plugins": {
    "banker": "{workspace}/skills/banker/web",
    "deliveries": "{workspace}/skills/deliveries/web"
  }
}
```

Plugin config supports simple (static only) or extended (with CGI hash) format:

```json
{
  "plugins": {
    "static-only": "/path/to/dir",
    "with-cgi": {
      "dir": "/path/to/dir",
      "hash": "sha256:abc123..."
    }
  }
}
```

- Plugin paths must be inside the workspace
- If CGI is enabled and a plugin has a `hash`, `index.py` at the plugin root handles all sub-paths — but only if its SHA-256 matches
- Plugins without a `hash` are static-only (CGI blocked even when globally enabled)
- Generate a hash: `shasum -a 256 /path/to/index.py`

## CGI Execution

**Off by default.** Enable in `config.json`:

```json
{
  "cgi": true
}
```

When enabled, only files named `index.py` can execute as CGI:

- **Webroot**: `index.py` in any subdirectory handles that directory's requests
- **Plugins**: `index.py` at the plugin root handles all plugin sub-paths
- **All other `.py` files** → 403 Forbidden (never served, never executed)
- Scripts must have the executable bit set (`chmod +x`)

## Security

- **Webroot isolation** — config.json is outside the webroot (`www/`), never served
- **CGI off by default** — must be explicitly enabled via `"cgi": true` in config.json
- **Path containment** — all resolved paths must stay within their base directory. Symlinks are followed but the resolved target is checked for containment.
- **Plugin allowlist** — only directories explicitly registered in `config.json` are served; must be inside workspace
- **CGI restricted to `index.py`** — no arbitrary script execution; plugin CGI requires SHA-256 hash in config.json. Webroot CGI does not require a hash (webroot files are under your direct control)
- **All `.py` files blocked** except `index.py` entry points (not served as text, not executed)
- **Host allowlist** — optional `allowed_hosts` restricts which `Host` headers are accepted
- **Token auth** — optional bearer token via `--token` flag or `config.json`. Browser clients visit `?token=SECRET` once → session cookie set → all subsequent navigation works. API clients use `Authorization: Bearer <token>` header.
- **Path traversal protection** — all paths resolved and validated before serving
- **Default bind: `127.0.0.1`** (loopback only). LAN access via `--host 0.0.0.0` requires both token auth and `allowed_hosts` in config.json.

## Workspace Detection

The server auto-detects the workspace by walking up from `$PWD` (or the script location) looking for a `skills/` directory. The detected path is printed on startup so you can verify it.

To skip autodiscovery, set `INTRANET_WORKSPACE` to the workspace root:

```bash
INTRANET_WORKSPACE=/path/to/workspace python3 scripts/intranet.py start
```

## Notes
- All state files are inside the workspace:
  - Config: `{workspace}/intranet/config.json`
  - PID: `{workspace}/intranet/.pid`
  - Runtime: `{workspace}/intranet/.conf`
  - Webroot: `{workspace}/intranet/www/`
- No files are written outside the workspace
- 30-second timeout on CGI execution (when enabled)

---

## 中文说明

# Intranet

轻量级本地 HTTP 文件服务器——无需 Apache/nginx,无需 root 权限。提供静态文件、挂载插件目录,并将 `index.py` 入口点作为 CGI 运行。

**入口点:** `{baseDir}/scripts/intranet.py`

## 安装

前置条件和安装说明请参见 [SETUP.md](SETUP.md)。

## 命令

```bash
python3 {baseDir}/scripts/intranet.py start                          # Start on default port 8080
python3 {baseDir}/scripts/intranet.py start --port 9000              # Custom port
python3 {baseDir}/scripts/intranet.py start --host 0.0.0.0            # LAN access (requires token + allowed_hosts)
python3 {baseDir}/scripts/intranet.py start --token SECRET            # Enable bearer token auth
python3 {baseDir}/scripts/intranet.py status                         # Check if running
python3 {baseDir}/scripts/intranet.py stop                           # Stop server
```

## 目录结构

```
{workspace}/intranet/
├── config.json          # Server config (NOT served)
└── www/                 # Webroot (served files go here)
    ├── index.html
    └── ...
```

配置文件位于 `{workspace}/intranet/config.json`,webroot 为 `{workspace}/intranet/www/`。配置文件绝不会通过 HTTP 暴露。

## 插件

插件将外部目录挂载到 URL 前缀上。在 `config.json` 中配置:

```json
{
  "plugins": {
    "banker": "{workspace}/skills/banker/web",
    "deliveries": "{workspace}/skills/deliveries/web"
  }
}
```

插件配置支持简单(仅静态)或扩展(带 CGI 哈希)格式:

```json
{
  "plugins": {
    "static-only": "/path/to/dir",
    "with-cgi": {
      "dir": "/path/to/dir",
      "hash": "sha256:abc123..."
    }
  }
}
```

- 插件路径必须位于 workspace 内部
- 如果启用了 CGI 且插件带有 `hash`,则插件根目录下的 `index.py` 处理所有子路径——但仅当其 SHA-256 匹配时
- 没有 `hash` 的插件是仅静态的(即使全局启用 CGI 也会被阻止)
- 生成哈希:`shasum -a 256 /path/to/index.py`

## CGI 执行

**默认关闭。** 在 `config.json` 中启用:

```json
{
  "cgi": true
}
```

启用后,只有名为 `index.py` 的文件可以作为 CGI 执行:

- **Webroot**:任意子目录中的 `index.py` 处理该目录的请求
- **插件**:插件根目录下的 `index.py` 处理所有插件子路径
- **所有其他 `.py` 文件** → 403 Forbidden(绝不提供,绝不执行)
- 脚本必须设置可执行位(`chmod +x`)

## 安全

- **Webroot 隔离** —— config.json 位于 webroot(`www/`)之外,绝不提供
- **CGI 默认关闭** —— 必须通过 config.json 中的 `"cgi": true` 显式启用
- **路径限制** —— 所有解析后的路径必须保持在其基目录内。会跟随符号链接,但会检查解析后的目标是否在范围内。
- **插件白名单** —— 仅提供在 `config.json` 中显式注册的目录;必须位于 workspace 内部
- **CGI 限制为 `index.py`** —— 不允许任意脚本执行;插件 CGI 需要 config.json 中的 SHA-256 哈希。Webroot CGI 不需要哈希(webroot 文件在你的直接控制之下)
- **所有 `.py` 文件均被阻止**,`index.py` 入口点除外(不作为文本提供,也不执行)
- **Host 白名单** —— 可选的 `allowed_hosts` 限制接受哪些 `Host` 头
- **Token 认证** —— 通过 `--token` 标志或 `config.json` 提供的可选 bearer token。浏览器客户端访问一次 `?token=SECRET` → 设置会话 cookie → 后续所有导航都可正常工作。API 客户端使用 `Authorization: Bearer <token>` 头。
- **路径遍历保护** —— 所有路径在提供前都会被解析和验证
- **默认绑定:`127.0.0.1`**(仅回环)。通过 `--host 0.0.0.0` 进行 LAN 访问需要同时配置 config.json 中的 token 认证和 `allowed_hosts`。

## Workspace 检测

服务器通过从 `$PWD`(或脚本位置)向上查找 `skills/` 目录来自动检测 workspace。检测到的路径会在启动时打印出来,以便你验证。

要跳过自动发现,请将 `INTRANET_WORKSPACE` 设置为 workspace 根目录:

```bash
INTRANET_WORKSPACE=/path/to/workspace python3 scripts/intranet.py start
```

## 注意事项
- 所有状态文件都在 workspace 内部:
  - 配置:`{workspace}/intranet/config.json`
  - PID:`{workspace}/intranet/.pid`
  - 运行时:`{workspace}/intranet/.conf`
  - Webroot:`{workspace}/intranet/www/`
- 不会在 workspace 之外写入任何文件
- CGI 执行有 30 秒超时(启用时)