[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80482":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":15,"starSnapshotCount":15,"syncStatus":14,"lastSyncTime":34,"discoverSource":35},80482,"tgnas","aahl\u002Ftgnas","aahl","TgNAS is an S3-compatible and WebDAV-capable gateway backed by Telegram storage and local SQLite metadata.","https:\u002F\u002Fzread.ai\u002Faahl\u002Ftgnas",null,"Go",106,19,2,0,1,10,31,4,3.9,false,"dev",true,[25,26,27,28,29,30],"nas","oss","s3-compatible","s3-storage","telegram","webdav","2026-06-12 02:04:02","\u003Ccenter>\u003Cimg alt=\"aahl\u002Ftgnas\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fa810a605-a518-4adf-8497-83a2aeb50dc4\" \u002F>\u003C\u002Fcenter>\n\n# TgNAS\n\n`tgnas` is an S3-compatible and WebDAV-capable gateway backed by Telegram storage and local SQLite metadata.\n\n## Service modes\n\nBy default, `tgnas` reads `data\u002Fconfig.yaml` and starts one HTTP server with both protocols enabled:\n\n```bash\ntgnas\n```\n\nThe S3 API is served at normal bucket paths. WebDAV is served under the configured WebDAV prefix, defaulting to `\u002Fdav\u002F`.\n\nSingle-protocol modes are available when you only want one API surface:\n\n```bash\ntgnas -c config.yaml s3\ntgnas -c config.yaml dav\n```\n\n`-c` is a short alias for `-config`. Passing both `-config` and `-c` in the same invocation is a usage error. `-debug` is a global flag, so it must appear before any subcommand.\n\n## Configuration\n\nThe default config path is `data\u002Fconfig.yaml`.\n\nCommon environment variables:\n\n- `TGNAS_LISTEN` overrides `server.listen` when `server.listen_env` is configured. Default is `:9000`.\n- `TGNAS_SECRET_KEY` is the example S3\u002FWebDAV credential secret.\n- `TGNAS_TELEGRAM_BOT_TOKEN` provides the Telegram bot token in the default Docker-oriented config.\n- `TGNAS_TELEGRAM_CHAT_ID` is the default bucket chat ID reference.\n- `TGNAS_SQLITE_PATH` can override the metadata SQLite path.\n\nBucket-level public read can be enabled for anonymous S3 object downloads:\n\n```yaml\nbuckets:\n  public-files:\n    chat_id: \"${TGNAS_TELEGRAM_CHAT_ID}\"\n    public_read: true\n```\n\n`public_read` defaults to `false`. When enabled, anonymous S3 clients may only `GET` and `HEAD` objects in that bucket when they already know the object key. Bucket listing, root bucket listing, writes, deletes, and WebDAV still require authentication.\n\nTrusted proxy configuration for reverse proxies (e.g. cloudflared, nginx):\n\n```yaml\nserver:\n  trusted_proxies:\n    - \"127.0.0.1\u002F32\"\n    - \"172.16.0.0\u002F12\"\n  trusted_proxy_hosts:\n    - \"s3.example.com\"\n```\n\nWhen the request's remote IP matches a `trusted_proxies` CIDR range, `X-Forwarded-Host` (or `Forwarded: host=`) is trusted and applied to the request regardless of the host value. When the forwarded host matches a `trusted_proxy_hosts` entry (case-insensitive), the request is trusted regardless of the remote IP. Either match is sufficient. `X-Forwarded-Proto` (or `Forwarded: proto=`) is also applied when the request is trusted. Without trust, forwarded headers are ignored.\n\nWarning: `trusted_proxy_hosts` trusts the forwarded host value itself, which is client-controlled unless a real proxy strips and sets it. Block direct untrusted access to TgNAS when using it. Prefer `trusted_proxies` CIDR ranges where possible.\n\nWebDAV configuration:\n\n```yaml\nwebdav:\n  # prefix: \"\u002Fdav\u002F\"\n```\n\nThe prefix must start with `\u002F`, is normalized to end with `\u002F`, cannot be `\u002F`, and cannot conflict with the first path segment of any configured bucket.\n\n## Docker\n\nRun it with the default Docker-oriented config in `data\u002Fconfig.yaml`:\n\n```bash\nmkdir -p data\nwget -P data https:\u002F\u002Fgithub.com\u002Faahl\u002Ftgnas\u002Fraw\u002Frefs\u002Fheads\u002Fdev\u002Fdata\u002Fconfig.yaml\n\ndocker run --rm -u root -v \"$PWD\u002Fdata:\u002Fapp\u002Fdata\" ghcr.io\u002Faahl\u002Ftgnas chown -R app:app \u002Fapp\u002Fdata\n\ndocker run -d \\\n  --name tgnas \\\n  -p 9000:9000 \\\n  -v \"$PWD\u002Fdata:\u002Fapp\u002Fdata\" \\\n  -e TGNAS_SECRET_KEY=\"your-s3-and-webdav-password\" \\\n  -e TGNAS_TELEGRAM_CHAT_ID=\"-1001234567890\" \\\n  -e TGNAS_TELEGRAM_BOT_TOKEN=\"123456:telegram-bot-token\" \\\n  ghcr.io\u002Faahl\u002Ftgnas\n```\n\nThe container runs as a non-root `app` user and uses `\u002Fapp` as its working directory. The mounted `data` directory must be writable by that container user because SQLite metadata is stored under `\u002Fapp\u002Fdata` by default. If SQLite fails to open or create `metadata.sqlite`, fix the host directory ownership or permissions before restarting the container.\n\n## Docker Compose\n\nThe included `docker-compose.yml` uses the published GHCR image and mounts `.\u002Fdata` to `\u002Fapp\u002Fdata`:\n\n```bash\ncat \u003C\u003C EOF > .env\nTGNAS_PORT_EXPOSED=9000\nTGNAS_SECRET_KEY=\"your-s3-and-webdav-password\"\nTGNAS_TELEGRAM_CHAT_ID=\"-1001234567890\"\nTGNAS_TELEGRAM_BOT_TOKEN=\"123456:telegram-bot-token\"\nEOF\n\ndocker compose run --rm -u root tgnas chown -R app:app \u002Fapp\u002Fdata\nwget -P data https:\u002F\u002Fgithub.com\u002Faahl\u002Ftgnas\u002Fraw\u002Frefs\u002Fheads\u002Fdev\u002Fdata\u002Fconfig.yaml\n\ndocker compose up -d\n```\n\nIf the host `data` directory is owned by root or another user, grant write access to the UID used by the container's `app` user, or use a permissions policy such as a writable group on `.\u002Fdata`. Do not make the config or SQLite directory read-only.\n\n## Authentication\n\nS3 keeps SigV4 authentication except for anonymous `GET` and `HEAD` object requests against buckets configured with `public_read: true`.\n\nS3 object `GET` and `HEAD` requests may also use SigV4 query-string authentication, commonly called presigned URLs. Presigned URLs use the existing configured credentials and support `X-Amz-Expires` values up to `604800` seconds (7 days). Presigned URLs do not authorize bucket listing, root listing, writes, deletes, copy operations, or WebDAV requests.\n\nA presigned object URL has the same path-style shape as normal S3 object access:\n\n```text\nhttps:\u002F\u002Fs3.example.com\u002Ftgnas\u002Ftest.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Date=...&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=...\n```\n\nIf TgNAS is behind a reverse proxy that changes the origin host, configure `trusted_proxies` or `trusted_proxy_hosts` so the verifier sees the external host that was signed.\n\nWebDAV uses HTTP Basic Auth and reuses `auth.credentials`:\n\n- username: `access_key`\n- password: resolved value of `secret_key_env`\n\n## Local metadata CLI\n\n`tgnas` also provides read-only local listing commands that inspect the configured SQLite metadata database. These commands do not start the HTTP server and do not contact Telegram.\n\n```text\ntgnas [-debug] [-c|-config config.yaml] ls [-n|-limit N] bucket[\u002Fprefix]\ntgnas [-debug] [-c|-config config.yaml] lsd [bucket[\u002Fprefix]]\ntgnas [-debug] [-c|-config config.yaml] bucket rename [--dry-run] old-bucket new-bucket\n```\n\n`ls` prints object keys, one per line. It defaults to 1000 results; `-limit N` and `-n N` set the maximum result count, and `0` means no overall result limit while still reading in pages internally.\n\n`lsd` without a path prints enabled bucket names. `lsd bucket\u002Fprefix` prints direct pseudo-directories under the prefix using `\u002F` as the delimiter.\n\n`bucket rename` renames a bucket in the SQLite metadata database. The target bucket name must exist in the current config file with the same `chat_id` as the source bucket metadata. `--dry-run` prints what would change without modifying data. A warning is printed to stderr if the source bucket still appears in the config file.\n\n## WebDAV behavior\n\nWebDAV exposes object prefixes as directories. `MKCOL \u002Fdav\u002Fphotos\u002F2026\u002F` creates a zero-byte directory marker object with key `2026\u002F`, preserving empty directories.\n\nSupported common operations include `OPTIONS`, `PROPFIND`, `GET`, `HEAD`, `PUT`, `DELETE`, `MKCOL`, `COPY`, and `MOVE`. `LOCK` and `UNLOCK` return not implemented, and `OPTIONS` does not advertise lock support.\n\n`COPY` and `MOVE` are metadata-only within the same bucket, including recursive directory copy\u002Fmove. They reuse existing Telegram file\u002Fchunk metadata rather than downloading and re-uploading content.\n\nBuckets are still created from config only. If a bucket remains in metadata after being removed from config, it is treated as an orphan: normal object access is forbidden, but `DELETE \u002Fdav\u002F{bucket}` or S3 `DELETE \u002F{bucket}` can clean up the local metadata record and associated object\u002Fchunk metadata.\n\nBucket `chat_id` values can be literal Telegram chat IDs or full environment-variable references such as `chat_id: \"${TGNAS_PRIVATE_CHAT_ID}\"`. Partial interpolation is not supported. If the referenced environment variable is unset or empty, the resolved `chat_id` is empty and config validation fails.\n\n## Links\n- https:\u002F\u002Fdeepwiki.com\u002Faahl\u002Ftgnas\n- https:\u002F\u002Fzread.ai\u002Faahl\u002Ftgnas\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Flinux.do\u002F\">\u003Cimg alt=\"Linux.do community\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLinux.do-community-0ea5e9?style=for-the-badge\">\u003C\u002Fa>\n\u003C\u002Fp>\n","TgNAS 是一个基于 Telegram 存储和本地 SQLite 元数据支持的 S3 兼容及 WebDAV 能力网关。该项目使用 Go 语言开发，能够通过 Telegram 作为后端存储来提供对象存储服务，并且支持 S3 和 WebDAV 协议，适用于需要将文件存储于 Telegram 并通过标准协议访问的场景。用户可以通过配置文件自定义监听地址、安全密钥、Telegram 机器人令牌等参数，同时支持单协议模式运行以及设置桶级别的公共读权限，以满足不同环境下的需求。此外，TgNAS 还允许配置可信代理以适应反向代理部署。","2026-06-11 04:00:54","CREATED_QUERY"]