/health
不需要鉴权
健康检查
健康检查,不需要鉴权。
{ "ok": true, "data": { "status": "ok", "version": "1.0.0", "time": "2026-10-22T18:00:00+08:00" } }
REST API · v1 · PNG 隐写
把图片和秘密一起塞进猫爪下的像素迷宫。喵~
https://maze.shizuku.plus/api/v1
需要重写规则;若使用 api.php 直连则为
https://maze.shizuku.plus/api.php/v1
multipart/form-data
像素迷宫 API 把「沿 Gilbert 曲线循环移动像素」的混淆、水印合成与 PNG tEXt 资源隐写,
打包成一组 HTTP 接口。写操作统一使用 multipart/form-data,成功时默认直接返回
image/png,加 format=json 则返回 JSON 信封。
1.0.0multipart/form-dataimage/png 或 application/json
上传体积、像素数、尺寸与资源数量的具体上限,以 GET /capabilities
返回的 limits 为准。
默认情况下 API 是开放的,仅按 IP 限流。生产环境建议开启 API Key。
在 config.php 里打开 api.require_key:
// config.php
'api' => [
'require_key' => true,
// ...
],
然后用命令行生成 Key:
php bin/apikey.php create "我的应用"
# ✓ 已创建 Key:nlk_xxxxxxxxxxxxxxxxxxxxxxxx
# 只显示这一次,请立刻保存
调用时把 Key 放在请求头里(二选一):
X-API-Key: nlk_xxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer nlk_xxxxxxxxxxxxxxxxxxxxxxxx
config.php 中的 api.rate_limit。每个时间窗口(默认 60 秒)内限制请求数,响应头会返回:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1730000000
超限返回 429 RATE_LIMITED。图片过大、像素数超限也会被拒绝,具体见错误码。
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1730000000
Content-Type: application/json
{
"ok": false,
"error": {
"code": "RATE_LIMITED",
"message": "请求过于频繁,已触发限流喵~",
"field": null
},
"request_id": "req_6f3c9a2b1d4e"
}
接口成功时直接返回 image/png,元信息放在响应头:
| 响应头 | 说明 |
|---|---|
X-Neko-Offset | 本次使用的偏移量(像素序号) |
X-Neko-Offset-Mode | golden / pixels / percent / meta / code |
X-Neko-Restore-Code | 还原码,例如 NL1-1500x1200-ZK8X-4F |
X-Neko-Width / X-Neko-Height | 输出图片尺寸 |
X-Neko-Watermark | 本次操作是否绘制了水印:0 / 1 |
X-Neko-Resources | 输出 PNG 中携带的资源数量 |
X-Neko-Elapsed-Ms | 服务端处理耗时 |
X-Neko-Filename | 建议的下载文件名 |
X-Request-Id | 请求 ID,便于排查 |
Content-Disposition | inline; filename="..." |
加 ?format=json,或设置 Accept: application/json,会返回:
{
"ok": true,
"data": {
"mode": "obfuscate",
"mime": "image/png",
"filename": "neko-1500x1200-obfuscated.png",
"image_base64": "iVBORw0KGgo...",
"data_url": "data:image/png;base64,iVBORw0KGgo...",
"width": 1500,
"height": 1200,
"offset": 1234567,
"offset_mode": "golden",
"restore_code": "NL1-1500x1200-ZK8X-4F",
"watermark": { "applied": true, "type": "text", "layer": "pre" },
"resource_count": 0,
"bytes": 987654,
"elapsed_ms": 812,
"warnings": []
},
"request_id": "req_6f3c9a2b1d4e"
}
{
"ok": false,
"error": {
"code": "INVALID_OFFSET",
"message": "偏移量必须在 0 到 1799999 之间喵~",
"field": "offset"
},
"request_id": "req_6f3c9a2b1d4e"
}
HTTP 状态码见错误码。
偏移、水印、资源三组参数可以在同一次请求里组合使用。
混淆的本质是沿着 Gilbert 曲线把像素循环移动 offset 个位置。
图片尺寸为 W × H 时,可选值范围是 0 … W*H-1。
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
offset_mode | string | golden | golden 黄金分割(推荐)/ pixels 绝对像素 / percent 百分比 |
offset | int/float | — | pixels 时是像素数;percent 时是 0–100 的百分比 |
restore_code | string | — | 还原码,传入后优先于 offset,并校验尺寸 |
embed_meta | bool | 0 | 是否把偏移量写进 PNG 的 tEXt 块(方便一键还原;会降低保密性) |
golden 的计算方式与网页版完全一致:
$offset = (int) round((sqrt(5) - 1) / 2 * $width * $height);
还原码格式:NL1-<宽>x<高>-<偏移量 base36>-<校验码>,
例如 NL1-1500x1200-ZK8X-4F。
还原码只是方便记录,不是加密;校验码用于发现抄写错误。
水印可以和混淆一起使用:
wm_layer=pre(默认):先画水印、再混淆。水印被卷进像素迷宫,还原后才可见。wm_layer=post:先混淆、再画水印。水印直接盖在噪点图上,打开就能看见,适合署名/防盗。| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
watermark | bool | 0 | 是否启用水印 |
wm_type | string | text | text 文字 / image 图片 |
wm_text | string | 猫羽雫 nya~ | 文字水印,最长 160 字符,支持换行 |
wm_font | string | default | 字体 ID,见 GET /capabilities 的 fonts |
wm_size | int | 0 | 字号(px),0 表示按图片短边自动计算 |
wm_color | string | #8fd7ff | 颜色,#RGB 或 #RRGGBB |
wm_opacity | float | 0.55 | 不透明度,0.05–1 |
wm_position | string | bottom-right | top-left top top-right left center right bottom-left bottom bottom-right tile |
wm_margin | int | 28 | 边距(px) |
wm_rotate | int | 0 | 旋转角度(度,逆时针为正),-180–180 |
wm_tile_gap | int | 160 | 平铺间距(px),wm_position=tile 时生效 |
wm_shadow | bool | 1 | 是否绘制柔和阴影,提高可读性 |
wm_layer | string | pre | pre / post,见上 |
wm_image | file | — | 图片水印文件(wm_type=image 时必填),支持 PNG/JPEG/WebP |
wm_scale | float | 0.18 | 图片水印宽度占主图宽度的比例,0.03–1 |
字体:内置「思源黑体 SC」子集(OFL 授权),也支持 config.php 中配置的系统中文字体。
若指定字体不可用会返回 WM_FONT_MISSING。
tEXt)
可以把任意文件一起塞进输出 PNG 的 tEXt 块,图片视觉效果不变,
文件体积会变大(Base64 约 +33%)。
| 参数 | 类型 | 说明 |
|---|---|---|
resources[] | file[] | 要嵌入的文件,可重复;最多 12 个,总计 12 MB |
strip_resources | bool | 还原时是否丢弃原文件里的资源,默认 0(保留) |
输出 PNG 里会额外写入 neko-resources、image / video / object
等 tEXt 块,因此也能被原版 PNGPKG 网页工具识别。
共 8 个接口。除 GET /health 与 GET /capabilities 外,
其余都以 POST 提交 multipart/form-data。
| 方法 | 路径 | 说明 | 鉴权 |
|---|---|---|---|
| GET | /health | 健康检查 | 不需要 |
| GET | /capabilities | 服务能力 | 不需要 |
| POST | /obfuscate | 混淆图片 | 按配置 |
| POST | /deobfuscate | 还原图片 | 按配置 |
| POST | /watermark | 只加水印 | 按配置 |
| POST | /embed | 嵌入资源 | 按配置 |
| POST | /extract | 读取资源 | 按配置 |
| POST | /inspect | 结构摘要 | 按配置 |
三步走:混淆 → 记下还原码 → 还原。
# 1. 混淆:默认返回 PNG 二进制,还原码在响应头里
curl -X POST "https://maze.shizuku.plus/api/v1/obfuscate" \
-F "image=@cat.png" \
-F "offset_mode=golden" \
-D headers.txt \
-o cat-obfuscated.png
# 2. 取出还原码(X-Neko-Restore-Code: NL1-1500x1200-ZK8X-4F)
grep -i '^x-neko-restore-code' headers.txt
# 3. 还原
curl -X POST "https://maze.shizuku.plus/api/v1/deobfuscate" \
-F "image=@cat-obfuscated.png" \
-F "restore_code=NL1-1500x1200-ZK8X-4F" \
-o cat-restored.png
/health
不需要鉴权
健康检查,不需要鉴权。
{ "ok": true, "data": { "status": "ok", "version": "1.0.0", "time": "2026-10-22T18:00:00+08:00" } }
/capabilities
不需要鉴权
返回服务能力:GD 版本、支持的格式、可用字体、尺寸限制等。不需要鉴权。
{
"ok": true,
"data": {
"version": "1.0.0",
"php": "8.3.36",
"gd": "bundled (2.1.0 compatible)",
"formats": ["png", "jpeg", "gif", "webp", "bmp", "avif"],
"fonts": [{ "id": "default", "label": "思源黑体 SC", "available": true }],
"limits": { "max_upload_bytes": 16777216, "max_pixels": 8400000, "max_dimension": 12000, "max_resources": 12, "max_resources_bytes": 12582912 },
"positions": ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right", "tile"],
"offset_modes": ["golden", "pixels", "percent"]
}
}
fonts 给出 wm_font 可用的字体 ID,limits 给出上传与资源上限,
positions 与 offset_modes 是枚举值来源。
/obfuscate
multipart/form-data
混淆图片(可同时加水印、嵌资源)。参数:image(必填)、
4.1–4.3 中的任意参数。
curl -X POST "https://maze.shizuku.plus/api/v1/obfuscate" \
-H "X-API-Key: nlk_xxx" \
-F "image=@cat.png" \
-F "offset_mode=golden" \
-F "watermark=1" \
-F "wm_text=猫羽雫 nya~" \
-F "wm_position=bottom-right" \
-F "wm_opacity=0.6" \
-F "embed_meta=1" \
-o cat-obfuscated.png
返回:默认 PNG 二进制;format=json 时返回 JSON。
/deobfuscate
multipart/form-data
还原图片。参数:image(必填)、offset / offset_mode /
restore_code 三选一(若文件里带 neko-labyrinth 元数据且
allow_meta=1,可省略)。
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
allow_meta | bool | 1 | 是否允许从 PNG 元数据中读取偏移量 |
strip_resources | bool | 0 | 是否丢弃原文件中的隐写资源 |
curl -X POST "https://maze.shizuku.plus/api/v1/deobfuscate" \
-H "X-API-Key: nlk_xxx" \
-F "image=@cat-obfuscated.png" \
-F "restore_code=NL1-1500x1200-ZK8X-4F" \
-o cat-restored.png
还原后的图片会保留原文件里的隐写资源(tEXt 块),但会移除失效的偏移量元数据。
尺寸必须与还原码/元数据记录一致,否则返回 RESTORE_CODE_MISMATCH。
/watermark
multipart/form-data
只加水印,不混淆。参数:image、4.2 中除
wm_layer 外的字段。
curl -X POST "https://maze.shizuku.plus/api/v1/watermark" \
-H "X-API-Key: nlk_xxx" \
-F "image=@cat.png" \
-F "wm_text=猫羽雫 ♡" \
-F "wm_position=tile" \
-F "wm_rotate=-18" \
-F "wm_opacity=0.35" \
-o cat-watermarked.png
返回:默认 PNG 二进制;format=json 时返回 JSON。
/embed
multipart/form-data
把一个或多个文件嵌进现有 PNG(不改像素)。
curl -X POST "https://maze.shizuku.plus/api/v1/embed" \
-H "X-API-Key: nlk_xxx" \
-F "image=@cover.png" \
-F "resources[]=@secret.pdf" \
-F "resources[]=@cat.mp4" \
-o cover-with-resources.png
resources[] 最多 12 个、总计 12 MB;超出返回 RESOURCE_LIMIT。
/extract
返回 JSON
读取 PNG 中的隐写资源,返回 JSON。data 为 data URL,可直接在浏览器里预览/下载。
curl -X POST "https://maze.shizuku.plus/api/v1/extract" \
-H "X-API-Key: nlk_xxx" \
-F "image=@cover-with-resources.png"
{
"ok": true,
"data": {
"width": 1200,
"height": 800,
"metadata": {
"offset": 480000,
"restore_code": "NL1-1200x800-...-..",
"watermarked": true
},
"resources": [
{
"name": "secret.pdf",
"mime": "application/pdf",
"size": 123456,
"keyword": "object",
"data": "data:application/pdf;base64,JVBERi0xLjQK..."
}
],
"chunks": [{ "keyword": "neko-resources", "length": 123 }]
},
"request_id": "req_..."
}
没有资源时返回空数组,不报错。
/inspect
返回 JSON
查看 PNG 结构摘要(不返回资源内容,适合大文件)。
{
"ok": true,
"data": {
"width": 1200,
"height": 800,
"bytes": 987654,
"metadata": { "offset": 480000, "restore_code": "NL1-..." },
"text_chunks": [{ "keyword": "neko-resources", "length": 123 }],
"resources": [{ "name": "secret.pdf", "mime": "application/pdf", "size": 123456, "keyword": "object" }]
}
}
选接口、填参数,实时拼出调用片段。这里只做字符串拼接, 不会向任何地址发起请求。喵~
浏览器端的 FormData 这里先用 fileInput.files[0] 代替,请换成你自己的
<input type="file">;图片接口默认返回二进制,加 ?format=json
可拿到 JSON。
以混淆一张 cat.png 为例,三种语言写法。
$ch = curl_init('https://maze.shizuku.plus/api/v1/obfuscate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: nlk_xxx'],
CURLOPT_POSTFIELDS => [
'image' => new CURLFile('/path/cat.png', 'image/png', 'cat.png'),
'offset_mode' => 'golden',
'watermark' => '1',
'wm_text' => '猫羽雫 nya~',
'wm_layer' => 'pre',
],
]);
$png = curl_exec($ch);
file_put_contents('/path/cat-out.png', $png);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n";
const fd = new FormData();
fd.append('image', fileInput.files[0]);
fd.append('offset_mode', 'golden');
fd.append('watermark', '1');
fd.append('wm_text', '猫羽雫 nya~');
const res = await fetch('https://maze.shizuku.plus/api/v1/obfuscate?format=json', {
method: 'POST',
headers: { 'X-API-Key': 'nlk_xxx' },
body: fd,
});
const json = await res.json();
if (!json.ok) throw new Error(json.error.message);
const img = document.querySelector('img');
img.src = json.data.data_url;
console.log('还原码:', json.data.restore_code);
import requests
with open('cat.png', 'rb') as f:
r = requests.post(
'https://maze.shizuku.plus/api/v1/obfuscate',
headers={'X-API-Key': 'nlk_xxx'},
files={'image': ('cat.png', f, 'image/png')},
data={'offset_mode': 'golden', 'watermark': '1', 'wm_text': '猫羽雫 nya~'},
timeout=120,
)
r.raise_for_status()
open('cat-out.png', 'wb').write(r.content)
print(r.headers['X-Neko-Restore-Code'])
出错时统一返回错误信封,error.code 与 HTTP 状态如下:
error.code | HTTP | 说明 |
|---|---|---|
BAD_REQUEST | 400 | 参数缺失或格式错误 |
UNAUTHORIZED | 401 | 缺少/无效的 API Key |
FORBIDDEN | 403 | 操作被禁止 |
NOT_FOUND | 404 | 接口不存在 |
METHOD_NOT_ALLOWED | 405 | 请求方法不对 |
UPLOAD_TOO_LARGE | 413 | 文件超过 max_upload_bytes |
UNSUPPORTED_TYPE | 415 | 不是受支持的图片格式 |
IMAGE_TOO_LARGE | 422 | 尺寸或像素数超限 |
INVALID_OFFSET | 422 | 偏移量非法 |
OFFSET_REQUIRED | 422 | 还原时没有提供偏移量,且文件里没有元数据 |
INVALID_RESTORE_CODE | 422 | 还原码格式错误或校验失败 |
RESTORE_CODE_MISMATCH | 422 | 还原码与图片尺寸不匹配 |
WM_INVALID_TEXT | 422 | 水印文字为空或超长 |
WM_FONT_MISSING | 422 | 字体不可用 |
WM_INVALID_COLOR | 422 | 颜色格式错误 |
WM_INVALID_POSITION | 422 | 位置参数非法 |
WM_IMAGE_INVALID | 422 | 图片水印不可用 |
RESOURCE_LIMIT | 422 | 资源数量或体积超限 |
PNG_INVALID | 422 | 不是合法的 PNG |
RATE_LIMITED | 429 | 触发限流 |
INTERNAL | 500 | 服务端异常(带 request_id,详见服务器日志) |
每个错误响应都带 request_id;INTERNAL 的具体原因请到服务器日志里按这个 ID 查。
tEXt 块,关键字 image / video / object
与原版互通;额外的 neko-resources 块用于保存文件名和 MIME(原版会忽略它)。
见仓库根目录的 README.md。最短流程:
# 1. 要求 PHP >= 8.1,扩展:gd、fileinfo、mbstring、zlib
php -v
php -m | grep -E 'gd|fileinfo|mbstring'
# 2. 目录权限
chmod -R 755 .
chmod -R 775 storage
# 3. 自检
php bin/doctor.php
# 4. 本地试跑
php -S 127.0.0.1:8080 -t .
Nginx / Apache:把 /api/v1/* 重写到 api.php 即可(不配置重写也能直接用
/api.php/v1);另外请让 upload_max_filesize 与 post_max_size
大于 limits.max_upload_bytes,否则大图会在进 PHP 之前就被丢掉。
这个工具也可以作为 MCP 服务器给 AI 使用。默认不需要 API Key;
MCP 与 HTTP API 共用 api.require_key 开关,开启鉴权后再补 Key 即可。
POST https://maze.shizuku.plus/mcp.php
JSON-RPC 2.0,支持 initialize、notifications/initialized、ping、
tools/list、tools/call。无状态,直接返回 JSON;图片参数用
image_base64(可带 data URL 前缀)。
| 工具 | 说明 |
|---|---|
obfuscate | 混淆,可加水印、嵌入资源 |
deobfuscate | 还原,优先使用还原码 |
watermark | 只加水印 |
embed / extract | 写入 / 读出 PNG 资源 |
inspect | 查看 PNG 结构摘要 |
service_info | 字体、限额、鉴权状态 |
{
"mcpServers": {
"neko-labyrinth": {
"command": "python",
"args": ["/path/to/neko-labyrinth/mcp/server.py"],
"env": {
"NEKO_API_BASE": "https://maze.shizuku.plus/api/v1"
}
}
}
}
开启了 API Key 的话:stdio 加 "NEKO_API_KEY": "nlk_xxx",远程 MCP 加请求头
X-API-Key。完整说明与工具参数见仓库 mcp/README.md。