Commit Graph

8 Commits

Author SHA1 Message Date
zhengchen.tao a4492d4938 fix(deps): drop explicit pin on Microsoft.IdentityModel.Tokens
Build Docker Image / build (push) Failing after 11m23s
Build Docker Image / deploy (push) Has been skipped
JwtBearer 10.0.7 transitively brings in Microsoft.IdentityModel.JsonWebTokens
9.x, which calls Base64UrlEncoder.Decode(ReadOnlySpan<char>, Span<byte>) —
an overload only present in Microsoft.IdentityModel.Tokens 9.x. The
explicit pin to 8.9.0 forced NuGet to downgrade Tokens to 8.9, producing
a runtime MissingMethodException on every JWT validation. Symptom: every
incoming request gets 401, never visible at default log level.

Drop the explicit pin so JwtBearer's transitive dep wins. The code only
uses SymmetricSecurityKey etc., which are stable across versions.
2026-05-17 13:47:26 +08:00
zhengchen.tao e5682037ac ci: cache-to use mode=min + ignore-error to unblock deploy
Build Docker Image / build (push) Failing after 11m5s
Build Docker Image / deploy (push) Has been skipped
Same fix as nas-auth: mode=max takes ~4min and times out on the NAS,
which marks the build as failure and skips deploy. mode=min is fast
and ignore-error keeps the build from failing on cache export.
2026-05-17 01:17:21 +08:00
zhengchen.tao a7688242a2 build: NuGet cache mount + buildx registry cache
Build Docker Image / build (push) Failing after 16m15s
Build Docker Image / deploy (push) Has been skipped
Mirror the cache infra added to nas-auth (commit a1ecba3):
- Dockerfile: `RUN --mount=type=cache,target=/root/.nuget/packages`
  for restore + publish, with syntax 1.6 directive.
- workflow: cache-from/cache-to registry cache mode=max, so buildkit
  layer cache survives across CI runs (fresh buildkit per run otherwise).

First run after this still pays full cost; each subsequent run should
drop ~50% off dotnet restore.
2026-05-17 00:35:15 +08:00
zhengchen.tao 37082e165d feat(oauth): expose RFC 9728 protected resource metadata endpoint
Build Docker Image / build (push) Failing after 15m17s
Build Docker Image / deploy (push) Has been skipped
Claude.ai's MCP OAuth client only sends the RFC 8707 `resource` param
when it has Protected Resource Metadata (PRM) to learn the resource
identifier from. Without it, nas-auth's /authorize rejected the request
with 'resource 必填'.

Adds /.well-known/oauth-protected-resource (RFC 9728) that returns the
resource URL + AS URL + supported scopes. ResourceUrl is configurable
via Mcp__OAuthDiscovery__ResourceUrl, falling back to request authority
when unset (works in local dev).
2026-05-16 17:35:57 +08:00
zhengchen.tao 6494684273 ci: deploy via direct mount, drop nas-infra clone
Build Docker Image / build (push) Has been cancelled
Build Docker Image / deploy (push) Has been cancelled
Deploy job now cd's into /volume1/docker/compose/obsidian-mcp (exposed
to runner via the new gitea-runner mount) instead of cloning nas-infra
to a temp dir. The clone approach couldn't deploy because .env.shared
is gitignored and only exists on NAS.
2026-05-16 17:24:23 +08:00
zhengchen.tao 6c0b9c66af docs(readme): 子域名改名为 obsidian-mcp.zhengchentao.win
域名规划统一:MCP 服务子域名与 repo 名一致。
- obs.zhengchentao.win → obsidian-mcp.zhengchentao.win
2026-05-06 23:41:01 +08:00
zhengchen.tao 2aefdbeef5 fix(docker): 用 useradd 替换 adduser(slim 镜像无 adduser)
Build Docker Image / build (push) Successful in 9m5s
Build Docker Image / deploy (push) Failing after 28s
Debian bookworm-slim 不带 adduser(perl wrapper 包),
导致 build 阶段 exit 127 (command not found)。
改用预装 passwd 包提供的 useradd,语义等价。
2026-05-06 23:28:28 +08:00
zhengchen.tao 28f9a54ba9 obsidian-mcp: 初次落地 Obsidian Vault MCP Server (.NET 10, read+write)
Build Docker Image / build (push) Failing after 9m21s
Build Docker Image / deploy (push) Has been skipped
把 Obsidian vault 通过 MCP 暴露给 Claude.ai,OAuth 走 nas-auth。
设计文档见 vault Coding/obsidian-mcp/obsidian-mcp 设计.md。
代码层落地参考 vault Coding/obsidian-mcp/MCP 实现指南.md。

V1+V2 同时实现(用户要求跳过分阶段直接全部):

读 Tools(需 scope=read:obsidian):
- list_vault_tree(一次性 vault 地图,限制深度)
- list_files / read_file(含 offset/limit 大文件分页)
- search(子串匹配 + glob 过滤,最多 50 hits)
- get_metadata(size / modified_at / has_frontmatter)

写 Tools(需 scope=write:obsidian):
- write_file / append_file
- 多重门禁:scope 校验 + 路径黑名单 + 写入白名单 + 永禁文件
  - 永禁写:任意目录的 AGENTS.md / PROFILE.md / README.md / CLAUDE.md / 01-Secret/**
  - 白名单:02-ShengquGames/logs/ + Coding/ + NAS/NAS 待办清单.md
- 写入审计日志按天 rotate(JSON line)

安全:
- VaultPathResolver chroot:path traversal + symlink 双拒绝
- JwtBearer (HS256, Current+Previous fallback, MapInboundClaims=false)
- aud=obsidian, iss=https://auth.zhengchentao.win
- 黑名单:01-Secret / .obsidian / .trash / .git

技术栈:
- .NET 10 + ModelContextProtocol SDK 1.0
- Streamable HTTP transport (POST /mcp)
- JwtBearer 10.0 + IdentityModel.Tokens 8.x

部署:
- Dockerfile multi-stage,runtime 装 ripgrep(V3 备用),non-root user
- .gitea/workflows/build-image.yml:build + deploy 双 job,buildkit v0.13.2
- 容器内 :8080,宿主端口 9090
- 子域名 obs.zhengchentao.win
- vault 挂载 /volume1/docker/webdav/data/Zhengchen:/vault:rw(V2 写入需要 rw)

测试:35/35 单测过(VaultPathResolver path traversal/blacklist/symlink + VaultWriteGuard whitelist/forbidden)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 01:32:11 +08:00