REST API · v1 · PNG 隐写

像素迷宫 API

把图片和秘密一起塞进猫爪下的像素迷宫。喵~

基础地址 https://maze.shizuku.plus/api/v1

需要重写规则;若使用 api.php 直连则为 https://maze.shizuku.plus/api.php/v1

  • HTTP/HTTPS
  • multipart/form-data
  • 返回 PNG 二进制或 JSON

概览

像素迷宫 API 把「沿 Gilbert 曲线循环移动像素」的混淆、水印合成与 PNG tEXt 资源隐写, 打包成一组 HTTP 接口。写操作统一使用 multipart/form-data,成功时默认直接返回 image/png,加 format=json 则返回 JSON 信封。

版本
1.0.0
协议
HTTP/HTTPS
请求体
multipart/form-data
响应
image/png 或 application/json
鉴权
默认开放,可选 API Key
限流
默认 60 次 / 60 秒,响应头回报

上传体积、像素数、尺寸与资源数量的具体上限,以 GET /capabilities 返回的 limits 为准。

鉴权

默认情况下 API 是开放的,仅按 IP 限流。生产环境建议开启 API Key。

开启校验

在 config.php 里打开 api.require_key:

php
// config.php
'api' => [
    'require_key' => true,
    // ...
],

生成 Key

然后用命令行生成 Key:

bash
php bin/apikey.php create "我的应用"
# ✓ 已创建 Key:nlk_xxxxxxxxxxxxxxxxxxxxxxxx
#   只显示这一次,请立刻保存

携带 Key

调用时把 Key 放在请求头里(二选一):

http
X-API-Key: nlk_xxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer nlk_xxxxxxxxxxxxxxxxxxxxxxxx
  • Key 仅保存 SHA-256 摘要,服务端无法找回明文。
  • 每个 Key 可以单独设置每分钟限额,默认继承 config.php 中的 api.rate_limit。
  • 关闭鉴权时,限流按客户端 IP 计算。

限流

每个时间窗口(默认 60 秒)内限制请求数,响应头会返回:

http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1730000000

超限返回 429 RATE_LIMITED。图片过大、像素数超限也会被拒绝,具体见错误码。

http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1730000000
Content-Type: application/json
json
{
  "ok": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "请求过于频繁,已触发限流喵~",
    "field": null
  },
  "request_id": "req_6f3c9a2b1d4e"
}

返回格式

3.1 图片响应(默认)

接口成功时直接返回 image/png,元信息放在响应头:

图片响应的响应头字段
响应头说明
X-Neko-Offset本次使用的偏移量(像素序号)
X-Neko-Offset-Modegolden / 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-Dispositioninline; filename="..."

3.2 JSON 响应

加 ?format=json,或设置 Accept: application/json,会返回:

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"
}

3.3 错误格式

json
{
  "ok": false,
  "error": {
    "code": "INVALID_OFFSET",
    "message": "偏移量必须在 0 到 1799999 之间喵~",
    "field": "offset"
  },
  "request_id": "req_6f3c9a2b1d4e"
}

HTTP 状态码见错误码。

参数约定

偏移、水印、资源三组参数可以在同一次请求里组合使用。

4.1 偏移量

混淆的本质是沿着 Gilbert 曲线把像素循环移动 offset 个位置。 图片尺寸为 W × H 时,可选值范围是 0 … W*H-1。

偏移量相关参数
参数类型默认说明
offset_modestringgoldengolden 黄金分割(推荐)/ pixels 绝对像素 / percent 百分比
offsetint/float—pixels 时是像素数;percent 时是 0–100 的百分比
restore_codestring—还原码,传入后优先于 offset,并校验尺寸
embed_metabool0是否把偏移量写进 PNG 的 tEXt 块(方便一键还原;会降低保密性)

golden 的计算方式与网页版完全一致:

php
$offset = (int) round((sqrt(5) - 1) / 2 * $width * $height);

还原码格式:NL1-<宽>x<高>-<偏移量 base36>-<校验码>, 例如 NL1-1500x1200-ZK8X-4F。
还原码只是方便记录,不是加密;校验码用于发现抄写错误。

4.2 水印

水印可以和混淆一起使用:

  • wm_layer=pre(默认):先画水印、再混淆。水印被卷进像素迷宫,还原后才可见。
  • wm_layer=post:先混淆、再画水印。水印直接盖在噪点图上,打开就能看见,适合署名/防盗。
水印相关参数
参数类型默认说明
watermarkbool0是否启用水印
wm_typestringtexttext 文字 / image 图片
wm_textstring猫羽雫 nya~文字水印,最长 160 字符,支持换行
wm_fontstringdefault字体 ID,见 GET /capabilities 的 fonts
wm_sizeint0字号(px),0 表示按图片短边自动计算
wm_colorstring#8fd7ff颜色,#RGB 或 #RRGGBB
wm_opacityfloat0.55不透明度,0.05–1
wm_positionstringbottom-righttop-left top top-right left center right bottom-left bottom bottom-right tile
wm_marginint28边距(px)
wm_rotateint0旋转角度(度,逆时针为正),-180–180
wm_tile_gapint160平铺间距(px),wm_position=tile 时生效
wm_shadowbool1是否绘制柔和阴影,提高可读性
wm_layerstringprepre / post,见上
wm_imagefile—图片水印文件(wm_type=image 时必填),支持 PNG/JPEG/WebP
wm_scalefloat0.18图片水印宽度占主图宽度的比例,0.03–1

字体:内置「思源黑体 SC」子集(OFL 授权),也支持 config.php 中配置的系统中文字体。 若指定字体不可用会返回 WM_FONT_MISSING。

4.3 资源隐写(PNG tEXt)

可以把任意文件一起塞进输出 PNG 的 tEXt 块,图片视觉效果不变, 文件体积会变大(Base64 约 +33%)。

资源隐写相关参数
参数类型说明
resources[]file[]要嵌入的文件,可重复;最多 12 个,总计 12 MB
strip_resourcesbool还原时是否丢弃原文件里的资源,默认 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结构摘要按配置

快速开始

三步走:混淆 → 记下还原码 → 还原。

bash
# 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
GET /health 不需要鉴权

健康检查

健康检查,不需要鉴权。

json
{ "ok": true, "data": { "status": "ok", "version": "1.0.0", "time": "2026-10-22T18:00:00+08:00" } }
GET /capabilities 不需要鉴权

服务能力

返回服务能力:GD 版本、支持的格式、可用字体、尺寸限制等。不需要鉴权。

json
{
  "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 是枚举值来源。

POST /obfuscate multipart/form-data

混淆图片

混淆图片(可同时加水印、嵌资源)。参数:image(必填)、 4.1–4.3 中的任意参数。

bash
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。

POST /deobfuscate multipart/form-data

还原图片

还原图片。参数:image(必填)、offset / offset_mode / restore_code 三选一(若文件里带 neko-labyrinth 元数据且 allow_meta=1,可省略)。

还原接口的专属参数
参数类型默认说明
allow_metabool1是否允许从 PNG 元数据中读取偏移量
strip_resourcesbool0是否丢弃原文件中的隐写资源
bash
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。

POST /watermark multipart/form-data

只加水印

只加水印,不混淆。参数:image、4.2 中除 wm_layer 外的字段。

bash
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。

POST /embed multipart/form-data

嵌入资源

把一个或多个文件嵌进现有 PNG(不改像素)。

bash
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。

POST /extract 返回 JSON

读取资源

读取 PNG 中的隐写资源,返回 JSON。data 为 data URL,可直接在浏览器里预览/下载。

bash
curl -X POST "https://maze.shizuku.plus/api/v1/extract" \
  -H "X-API-Key: nlk_xxx" \
  -F "image=@cover-with-resources.png"
json
{
  "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_..."
}

没有资源时返回空数组,不报错。

POST /inspect 返回 JSON

结构摘要

查看 PNG 结构摘要(不返回资源内容,适合大文件)。

json
{
  "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" }]
  }
}

代码生成器

选接口、填参数,实时拼出调用片段。这里只做字符串拼接, 不会向任何地址发起请求。喵~

bash

浏览器端的 FormData 这里先用 fileInput.files[0] 代替,请换成你自己的 <input type="file">;图片接口默认返回二进制,加 ?format=json 可拿到 JSON。

示例代码

以混淆一张 cat.png 为例,三种语言写法。

PHP

php
$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";

JavaScript(浏览器 / Node 18+)

js
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);

Python

python
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 状态如下:

错误码与 HTTP 状态
error.codeHTTP说明
BAD_REQUEST400参数缺失或格式错误
UNAUTHORIZED401缺少/无效的 API Key
FORBIDDEN403操作被禁止
NOT_FOUND404接口不存在
METHOD_NOT_ALLOWED405请求方法不对
UPLOAD_TOO_LARGE413文件超过 max_upload_bytes
UNSUPPORTED_TYPE415不是受支持的图片格式
IMAGE_TOO_LARGE422尺寸或像素数超限
INVALID_OFFSET422偏移量非法
OFFSET_REQUIRED422还原时没有提供偏移量,且文件里没有元数据
INVALID_RESTORE_CODE422还原码格式错误或校验失败
RESTORE_CODE_MISMATCH422还原码与图片尺寸不匹配
WM_INVALID_TEXT422水印文字为空或超长
WM_FONT_MISSING422字体不可用
WM_INVALID_COLOR422颜色格式错误
WM_INVALID_POSITION422位置参数非法
WM_IMAGE_INVALID422图片水印不可用
RESOURCE_LIMIT422资源数量或体积超限
PNG_INVALID422不是合法的 PNG
RATE_LIMITED429触发限流
INTERNAL500服务端异常(带 request_id,详见服务器日志)

每个错误响应都带 request_id;INTERNAL 的具体原因请到服务器日志里按这个 ID 查。

兼容性

  • 本工具的混淆算法与原版 PNGPKG 完全一致:同样的尺寸 + 同样的偏移量,可以互相还原。
  • 隐写使用 PNG tEXt 块,关键字 image / video / object 与原版互通;额外的 neko-resources 块用于保存文件名和 MIME(原版会忽略它)。
  • 输出总是 8-bit RGBA PNG;原图是 JPEG/WebP/GIF 时会先转成 PNG,透明通道会保留。

自部署

见仓库根目录的 README.md。最短流程:

bash
# 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

这个工具也可以作为 MCP 服务器给 AI 使用。默认不需要 API Key; MCP 与 HTTP API 共用 api.require_key 开关,开启鉴权后再补 Key 即可。

远程端点(Streamable HTTP)

endpoint
POST https://maze.shizuku.plus/mcp.php

JSON-RPC 2.0,支持 initialize、notifications/initialized、ping、 tools/list、tools/call。无状态,直接返回 JSON;图片参数用 image_base64(可带 data URL 前缀)。

MCP 工具
工具说明
obfuscate混淆,可加水印、嵌入资源
deobfuscate还原,优先使用还原码
watermark只加水印
embed / extract写入 / 读出 PNG 资源
inspect查看 PNG 结构摘要
service_info字体、限额、鉴权状态

本地 stdio(Claude Desktop / Cursor)

json
{
  "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。