Releases: gentpan/LitePic
LitePic v3.3.8
Added
- 上传限制重新回到后台可配置:单文件上限、单次队列数量上限、上传并发数统一由设置页管理,并同步写入 .user.ini。
Changed
- 批量上传默认并发调整为 3,延长上传超时,减少大批量图片上传时的连接中断。
- 批量上传进度刷新不再反复重建队列 DOM,降低大量图片队列下的浏览器负担。
- 413 / 连接中断上传错误提示更明确。
Fixed
- 修复登录态缓存导致首页误判未登录的问题。
- 修复设置页 AJAX 异常时只显示“服务器返回格式异常”的问题。
- 修复 UPTIME 30 天 / 90 天切换被缓存影响的问题。
v3.3.7 — Gallery Inline Rename + Deploy Script
[3.3.7] - 2026-05-15
Added
- 图库卡片文件名内联重命名 — 双击
.img-name进入编辑态(contenteditable),Enter 保存,Esc 取消。后端新 actionrename,只改images.original_name列,磁盘上的随机 hash 文件名 + 老链接 + 远程同步 key 都不动。扩展名始终保留原始的(用户输入的后缀会被剥掉)防 MIME 漂移。文件名清洗复用 Telegram caption 的白名单:剥目录段 + 去 control / 路径分隔符 / shell 元字符,最长 120 字符。事件在document上代理,所以 PJAX 重渲染后的新卡片自动获得行为。
Tooling
bin/deploy.sh一键部署脚本 — 流式 tar 推 + 服务器端快照 + 维护模式 + 触发迁移 + HTTP 自检 + 一键回滚命令。tar excludes 同时挡 macOS 元数据(._*、.DS_Store)和站点状态(data/、uploads/、logs/、.env、.user.ini、.htaccess)。支持--dry-run/--skip-build/--skip-snapshot。全部参数 env var 化(LITEPIC_HOST/USER/KEY/REMOTE/OWNER/URL)。背景:之前手动 tar | ssh tar 推到 Linux 时,BSD tar 的 pax 扩展头被 GNU tar 转成 169 个._FILENAME影子文件落盘,包括._index.php/._worker.php这种会被 nginx 当静态文件 serve 出去的。新脚本同时设COPYFILE_DISABLE=1防 BSD tar 写元数据,部署前还 SSH 一次兜底清掉服务器残留._*。
v3.3.6 — Security + Concurrency Hardening
[3.3.6] - 2026-05-15
集中修复 v3.3.4 引入的安全 + 并发问题。整轮代码审查共发现 6 个 P0 / 8 个 P1 / 8 个 P2,本版本全部修复并加配套迁移 + worker 兜底清理。
Security
/i/<filename>现在受相册可见性约束 — 私密 / 密码相册里的图片不再能通过直链绕开密码门。ImageServeService新增isAlbumAccessAllowed()闸门:图片不在任何相册 / 至少在一个 public / unlisted 相册时正常服务;只在 private 相册时仅管理员可见;只在 password 相册时检查lp_album_<key>HMAC cookie。返回 404(而非 403)避免攻击者通过响应码枚举受限文件名。- Telegram webhook 三层防御真正各自生效 — 之前 header secret 在 header 缺失时直接通过("三层"实际只有两层),URL secret 校验在 feature gate 之前所以禁用部署能被指纹识别,
from.id完全可由攻击者伪造。本版本把 URL secret + header secret + feature gate 全部串行化、统一返回 403 + 无 body,把 enabled / disabled / bad-secret 三种状态压成同一种响应。 - Telegram 下载加 size cap —
TelegramApi::downloadFile现在用CURLOPT_MAXFILESIZE+CURLOPT_PROGRESSFUNCTION双重防护,白名单内的攻击者也无法借image/pngMIME 标签的 2 GB document 把sys_get_temp_dir()撑爆。默认上限继承MAX_FILE_SIZE。 UploadService::storeFromPath()加路径白名单 + 拒绝 symlink — 之前任意可读路径都能被吞,一个未来调用方不小心传了$_POST['path']就成任意文件读取。Telegram 调用方现在显式传[sys_get_temp_dir()],默认空数组也会回退到 tmp 目录而非"放行一切"。- Webhook 注册成功 banner 不再泄露 URL secret —
Webhook 已注册,URL: …/webhook/abcd…1234替代原来的完整明文 URL,view-source / 浏览器历史里都看不到完整 secret 了。
Fixed (correctness)
- 导入队列的 row-claim 真正原子化 —
ImportQueueRepository::nextBatch()用UPDATE ... WHERE id IN (SELECT ... LIMIT)一次性把 pending 行 flip 到processing并打上worker_id,然后只 SELECT 自己标记的那批。in-request shutdown drain 和 cron worker 同时跑时不会再双倍处理。新增reclaimStale()在每轮 drain 开头把 >10 分钟还在processing的行还回pending(覆盖 worker 进程中途死亡的孤儿行)。 - 同步缩略图加 wall-clock + 像素双闸门 —
ThumbnailService::create()拒绝 > 60 MP 的图(那些走异步),createWithImagick()设置Imagick::RESOURCETYPE_TIME=8s+MEMORY=256MB+MAP=512MB。之前一张多页 TIFF 能让上传请求跑 30s+ 直接撞 XHR timeout,前端报失败但服务端记录已写入 → 留下"幽灵"图片。现在 8 秒做不完直接 fail,异步 worker 接手。 - 缩略图 alpha-skip 改用 MIME 而非扩展名 — 之前
transparent.png改名.jpg后,thumbnail 跳过 alpha 剥离 → 输出黑底。现在用finfoMIME 判定。 - Telegram
/newalbum,/use加 update_id dedupe — 新表telegram_seen_updates通过 PK 上的INSERT OR IGNORE做幂等闸门。网络抖动导致的 Telegram 24h retry 不再创建重复相册。Photo 上传本身已经有 SHA1 dedupe,这次覆盖剩下的 mutating 命令。 - Telegram tempfile 用 try/finally 清理 — 之前
storeFromPath在 Imagick OOM 时抛 throwable,后面的@unlink($tmp)永远跑不到。/tmp残留垃圾在长期运行的主机上会累积。 - Telegram caption-derived filename 加 sanitize —
basename()+ 去 control / 路径 / shell 元字符 + 120 字符截断。文件存盘名仍由PathService::generateFilename()生成,这里护的是images.original_name列在管理 UI 里的 XSS 表面。 /a/<id>在php -S下不再 404 —router.php跟app/Http/router.php的正则现在一致:^/a/(\d+|[a-z][a-z0-9-]{0,49})/?$+$_GET['album_key'](canonical)。nginx 路由和 PHP 内置服务器路由不再分裂。- 迁移 012 用 regex 重写 + 写后断言 — 不再依赖字面量空白匹配。之前迁移 010 一改格式,012 就会静默 no-op,slug 仍是 NOT NULL,后续插入失败。现在用
/\bslug\s+TEXT\b[^,\n)]*?\bNOT\s+NULL\s*/i匹配,写完检查PRAGMA table_info强行抛错。 - 相册访问计数:从 cookie-only 改成 IP + cookie 双层 — 新表
album_visit_log(album_id, ip_hash, bucket_at)作为 source of truth,cookie 退居 UX 快路径。隐身窗 / 清 cookie 不能再无限刷计数。IP 用 sha1(ip + ADMIN_SESSION_SECRET) 存,不留明文 IP。
Changed (performance / hygiene)
LivenessTracker::recordOnce()按需写入 — 主机/proc/uptime可读时直接 short-circuit(uptime UI 已经走 OS 数据,无需写表了),每请求一次的 INSERT 减为 0。沙盒主机(BT 锁 /proc / Windows-PHP)仍正常写,后续series()fallback 也仍工作。ImageInfo::preload()批量预热 — public album / album_edit 渲染 500 张相册之前 = 500 次SELECT ... WHERE filename = :fround-trip。新增preload(array $filenames)走一条IN (...)全拉回来塞进 per-instance 缓存。两个页面已切换到新流程。ServerInfo::databaseSummary()加 per-process 缓存 — 设置页一次渲染期内重复调用不再每次对每表跑COUNT(*)。新请求总是看到新快照。/api/v1/uptime加 60s 浏览器缓存 — 数据本来就是分钟粒度,后端无谓重算。- 浏览器并发上传 20 → 8 — 跟 PHP-FPM warm workers 对齐,避免 9-20 号 XHR 在 accept 队列里等到 timeout。
- worker.php 清理三张小表 — 每次 cron 跑完 drain 后顺手清:
liveness_pings> 90d、telegram_seen_updates> 24h(Telegram retry 窗口)、album_visit_log> 1h(30min dedupe bucket + 安全裕量)。三张表都不再无界增长。
UI
- 深色模式 Toast 取消渐变 — 顶部
notification-process / convert / compress和右侧notification-containertoast 在深色下都改成纯深色底(#0f1116),只保留左侧 4px accent 竖条 + icon 的素色识别。之前的 135deg / 90deg 渐变在 OLED 屏上发糊,侧边 toast 还有个 bug 在深色下渐变到#ffffff白色。浅色模式渐变完整保留。修了一同处:侧边 toast 在深色下的标题/副本/关闭按钮字色从黑字切到浅字(之前在白色渐变末端能勉强读到,改纯深色底后必须切)。
Infrastructure
.user.ini/nginx-litepic.conf与 CHANGELOG 3.3.4 宣传值对齐 — 之前实际是upload_max_filesize=20M/client_max_body_size 50m,文档却写 200M。现在统一改成:upload_max_filesize=200M、post_max_size=220M、max_input_time=600、client_max_body_size 200m、client_body_buffer_size 32m、client_body_timeout 600s。- 新迁移 014_idempotency_tables.php — 一次性创建
telegram_seen_updates+album_visit_log两张小表,worker 自带 retention。 - 新仓库类 —
app/Repository/TelegramSeenUpdateRepository.php、app/Repository/AlbumVisitLogRepository.php。 - 新 helper —
app/Repository/AlbumImageRepository::visibilityFor()、app/Repository/ImageRepository::findMany()、app/Service/Image/ImageInfo::preload()、app/Service/Upload/UploadService::pathInsideAny()、app/Service/Stats/LivenessTracker::osUptimeAvailable()、app/Repository/ImportQueueRepository::reclaimStale()。
v3.3.4 — Telegram bot + album-by-id + 10× upload speed
Added
- Telegram 机器人集成 — 全新
设置 → Telegramtab,绑定一个 BotFather 拿到的 Bot Token + 白名单用户 ID 后,机器人即可:- 直接发图片(或图片文档)到机器人 → 自动上传到 LitePic,回复公开链接
/list [N]、/albums、/album <key>、/newalbum <名称>、/use <key>、/me、/help一整套指令- 三层安全:URL secret(32-hex,一键轮换) + Telegram
secret_tokenheader +TELEGRAM_ALLOWED_USER_IDS白名单 - 每用户「默认上传相册」状态(新表
telegram_user_state) - 新文件:
app/Service/Telegram/{TelegramApi,TelegramHandler}.php、app/Repository/TelegramUserStateRepository.php、api/telegram.php、迁移013_telegram_settings.php
- 底部批量进度卡 —
assets/js/main.jsImgEt.BatchProgress单例 + 新 CSS。批量压缩 / 转换 / 删除时,顶部 toast 只发状态广播,底部固定卡显示实时 N/M + 进度条 + 百分比;三色 variant(绿/紫/红)对应三种操作。 UploadService::storeFromPath()公共方法 — 服务端 ingest 路径,把已经在磁盘上的文件走完整上传流水线(供 Telegram 用,未来也可供 CLI 导入)。- nginx 详细 timing log_format —
wall_time+upstream_time字段,以后排查上传性能问题不用靠猜。
Changed
- 相册 URL 默认数字 ID,slug 可选 — 迁移
012_albums_slug_nullable.php通过writable_schema把albums.slug改成 NULLABLE。新建相册不填 slug → 公开 URL 是/a/<id>(数字);填 slug →/a/<slug>。Router、Controller、Repository 全部支持findByKey($key)双模式。新增AlbumService::urlKey($album)作为单一 URL 构造源。 - 上传性能 10× 优化 — 综合多层调优,典型 2-3MB 照片从「100 秒级」体感降到「1-3 秒级」:
- PHP-FPM
pm = ondemand → dynamic(8 worker 常驻,消除 burst 冷启动 50-150ms) ThumbnailServiceImagick 加jpeg:sizeDCT-domain 降采样 hint + JPEG 跳过mergeImageLayers / setImageAlphaChannel(4032×3024 大图缩略图 794ms → 74ms,10×)- 同步生成缩略图 —
UploadService::storeFromPath/handleSingle在响应内做完缩略图,响应一返回前端立刻显示真实 thumbnail URL,无需轮询;失败自动退回到异步队列 getimagesize透传给ThumbnailService::create($id, $force, $info)避免重复读图片头LivenessTracker::recordOnce()改 1/8 抽样,burst 上传时 SQLite 写锁竞争 -87.5%(uptime 桶分辨率仍保持分钟级)- 浏览器并发上传
MAX_CONCURRENT: 3 → 20(自托管/局域网图床,带宽是自己的) - 后端处理状态轮询从
setInterval(2500ms)改成指数退避400 / 700 / 1100 / 1700 / 2500ms(缩略图典型 1-2 次内拿到 'done',总等待 < 1 秒) - 新增
cron兜底:每分钟跑worker.php处理队列(用户离开页面后还能继续 drain)
- PHP-FPM
- 基础设施门限 — PHP
upload_max_filesize/post_max_size50M → 200M,memory_limit128M → 256M,max_input_time60s → 600s;nginxclient_max_body_size50m → 200m,client_body_buffer_size512k → 32m(32MB 以下完全在内存,不再 spool 到磁盘) - 删除相册 — 改用自定义
ImgEt.DialogManager.showConfirmDialog(danger: true),不再用浏览器原生confirm(),跟删除图片走同一套样式。 - 能力卡帮助图标 —
?链接直接跳litepic.io/docs#<anchor>,移除之前在应用内做但有 CSS 渲染问题的「一键启用命令」弹窗。文档统一在官网维护。
Fixed
- 修复迁移
012早期版本里PDO->beginTransaction()在 Migration runner 已开启的事务里抛 "There is already an active transaction" — 改用writable_schema+schema_versionbump 在已有事务里直接修改sqlite_master。 - 修复某些情况下
findByHashWithBackfill在历史无 hash 图片很多时遍历整库 sha1_file 导致上传慢(本版本未改 API,但 sync thumbnail 的延迟提升让这个潜在风险显形 — 若用户报告慢可建议跑一次 backfill 命令)。
Infrastructure
- 服务器侧调优(部署文档已更新):BT 面板用户建议
pm = dynamic,nginxclient_body_buffer_size 32m,PHPmemory_limit 256M,以及 cron* * * * * php /<root>/worker.php。
LitePic v3.3.1
Added
- 程序自动更新:后台从 GitHub Release 下载 ZIP,只替换程序文件,保护 .env、.user.ini、.htaccess、data、uploads、logs、static/images。
- 新增 /api/v1/update/check 和 /api/v1/update/install。
Changed
- 版本号统一升至 3.3.1,页脚、版权说明和后台更新面板读取同一个 LITEPIC_VERSION。
- 系统状态接口统一到 /api/v1/system/status。
- 清理废弃背景上传样式、access.log 旧配置和 Docker 调度文案。
Validation
- 全量 PHP 语法检查通过。
- assets/js/main.js 与 assets/js/main.min.js 通过 node --check。
- CSS min 产物已同步。
LitePic v3.2.0
LitePic v3.2.0
本次更新
- README 完全重写,精简结构,聚焦核心特性与快速开始流程
- 配置文件清理,移除冗余旧文件
- 版本号升至 3.2.0
升级说明
查看 CHANGELOG.md 了解完整变更。
LitePic v3.1.0 — 异步处理 / Passkey / 数据库备份 / 多 Web 服务器
主要新特性
异步处理流水线 — 上传请求把图片入库后立即返回,缩略图 / 压缩 / WebP / AVIF / 水印 / R2 同步全部走 SQLite 队列后台 drain。worker.php 可作为 docker compose sidecar 长驻或 cron 拉起,跟 in-request drain 共享同一数据库。ResponseDetacher 让 PHP-FPM / LiteSpeed 在响应发出后继续跑后处理。
Passkey / WebAuthn 登录 — 指纹 / Face ID / 硬件 key 无密码登录,支持注册多个凭证。自实现 165 行 CBOR 解码器和 ES256 签名验证,零 composer 依赖。
SQLite 数据库备份 — VACUUM INTO 在线热备,支持手动 / 定时调度 / 保留份数 / R2/S3 异地容灾 / UI 一键恢复。
残留数据清理 — 设置 → 数据库 tab 新增「扫描 + 清理」流程,5 类残留可选择性清理,绝不动磁盘文件 / 活动队列 / 设置 / Token / Passkey。
图库卡片右键菜单 — 重新生成缩略图 / 复制链接 / 下载原图、WebP、AVIF、缩略图 / 转换反向偏好格式(默认 WebP 时菜单显示「转换 AVIF」反之亦然)。其他位置右键仍是浏览器原生菜单。
自定义 URL 前缀 — 后台可把 `/uploads/` 改成 `/img/`、`/photo/`、`/p/foo-bar/` 任意单词前缀,全部经 `image.php` 走访问统计。Apache、Nginx、OpenResty、Caddy、PHP `-S` 五种部署方式都有等价配置随仓库分发。
Web 服务器自动检测 + 版本号 — 服务器信息 section 显示当前 Nginx / OpenResty / Caddy / Apache / LiteSpeed 类型 + 版本号;能力卡片(GD / ImageMagick / AVIF / WebP / 上传上限)未启用时带 `?` 图标链到 litepic.io/docs 对应章节。
兼容性变化
- 文件名格式:从 `uniqid() + '_NNN'` 改成 `bin2hex(random_bytes(16))` 32 位十六进制。老文件名保持不变,只影响新上传。
- 配置存储:所有后台开关从 `.env` 移到 SQLite `settings` 表。`.env` 仍作为首次安装的初始默认值来源;装好之后改设置不需要重启。
- 本地 `/docs` 和 `/api` 页面下线:完整文档迁移到 litepic.io/docs 和 litepic.io/api。
- 首页背景:移除后台动态切换 UI,固定从 `static/images/background.jpg` 读取。
- 「最大上传大小」输入框删除:改成纯展示服务器实际允许的值;调大请改 PHP-FPM / Web 服务器配置(详见 docs)。
UI 优化
- 设置 7 个 tabs 全部用路径化 URL(`/settings/`)+ PJAX 切换不刷整页 + Plan A 直角设计
- 转换按钮按后台「转换优先格式」自动切换显示
- 压缩 toast 显示实际调用的引擎(GD / TinyPNG / ImageMagick)
- 页脚 Powered by LitePic shields.io 风格徽章(Ubuntu 字体 + 黑底品牌蓝)
- Favicon 切到小米 2021 同款 squircle 风格(n≈4 superellipse)
- 多处图标 + 中文对齐错位修复(equal-height inline-flex 模式 + 1px 校准)
- 图库分页箭头改用 FA 矢量图标(替代 `«‹›»` HTML 实体)
完整变更见 CHANGELOG.md。
LitePic V3.0.0
Changelog
All notable changes to this project will be documented in this file.
[3.0.0] - 2026-04-29
Breaking Changes
- CSS Architecture: Migrated from custom CSS modules to Tailwind CSS v4 with CSS-first configuration. All legacy
assets/css/modules/*.cssfiles removed. - Documentation Routes: Split
docs.phpintousage.php+api.php. Routes changed from/docsto/docs(usage) and/api(API docs).
Added
- Tailwind CSS v4 build pipeline with
@tailwindcss/cliand component-layer CSS architecture. - Gallery link in navigation bar for logged-in users (
/gallery). - Custom scrollbar theming for WebKit and Firefox, following light/dark mode.
- Favicon suite: apple-touch-icon, Android Chrome icons (192x192 / 512x512), favicon-16x16, favicon-32x32, and
site.webmanifest. - Light/dark theme-aware logo switching via CSS custom properties (avoids Tailwind v4 Lightning CSS optimization bugs).
- Scrollbar gutter stabilization (
scrollbar-gutter: stable) to prevent layout shift.
Changed
- Logo: Replaced SVG logo with PNG (
logo.png/logo-dark.png) for better cross-browser rendering. - Footer layout: Reorganized into three-column layout—GitHub link on the left, centered copyright/stats/docs/API/login/theme-toggle row.
- Navigation bar:
- Guest: Home + Stats + Upload (CTA)
- Logged-in: Home + Stats + Gallery + Settings + Upload (CTA)
- Docs/API links moved to footer.
- Home page hero card: Light mode now uses the same glassmorphism effect as dark mode (semi-transparent white gradient + backdrop blur).
- Upload button colors: Fixed light-mode CTA button to use
#0052D9background with white text. - Documentation: Restructured usage guide into 7 chapters with AVIF/Passkey/fullscreen-upload coverage and environment config cheat sheet.
Fixed
- Dark-mode selector optimization bug: Replaced
html[data-theme="dark"]element selectors with CSS custom properties to avoid Lightning CSS dropping dark-mode prefixes. - Login panel display conflict: Removed HTML
hiddenclass to prevent Tailwind v4 utility layer from overriding component layer. - Transform property conflict: Notification animations now use
--tw-translate-x+translateproperty instead oftransform, avoiding conflicts with Tailwind v4 independent transform properties.
[2.3.0] - 2025-03-28
Added
- AVIF format support via ImageMagick.
- Preferred format setting (WebP / AVIF) in settings panel.
- Fullscreen upload mode.
- Distro detection in system status.
- Self-healing
open_basedirsandbox.
Changed
- UI overhaul with improved settings panel.
[2.2.0] - 2025-02-20
Added
- WebAuthn / Passkey login support.
- Docker support with Dockerfile and docker-compose.yml.
Fixed
- Session headers-already-sent warning in Docker.
- Root
.htaccessphp_flag engine offissue.
v2.3.3 — accurate system metrics & self-healing sandbox setup
The settings page now correctly reports OS, CPU cores, memory and uptime on hosts that wrap PHP in an open_basedir sandbox (宝塔/aaPanel/1Panel default config), and walks the operator through the one config tweak it can't make on its own.
Highlights
Distro detection
- System version row now shows "Debian 13" + the right FontAwesome Brands icon instead of
Linux 6.12.74+deb13+1-amd64. - Supported brand marks: Debian / Ubuntu / Fedora / CentOS / Red Hat / SUSE / openSUSE / Arch / Alpine. Anything else falls through to
fa-linux. - Reads
/etc/os-releasewhen reachable; sniffsphp_uname('v')/php_uname('r')for the brand + version when the file is sandboxed.
Sandbox-friendly metrics
- Memory and CPU-cores now read
/proc/meminfoand/proc/cpuinfodirectly via@file_get_contents— no longer dependent onshell_exec, which most hardened PHP setups ban. - Uptime text is derived from
$uptime_seconds(e.g. "2 天 3 小时") so it works without theuptimecommand. is_readable()probes that leakedopen_basedirwarnings have been removed;@file_get_contentsdoes the right thing on its own.
AVIF detection
- Capability check now ORs
Imagick::queryFormats('AVIF')withimageavif()(GD), so hosts with Imagick AVIF support stop falsely reporting "AVIF 未启用".
Self-healing setup banner
- When the sandbox blocks
/proc/{cpuinfo,meminfo,uptime}, the settings page renders a top banner with a 「一键修复」 button. - Clicking it appends the 3 paths to the site's
.user.ini(auto-backup first), all done by LitePic itself — no ssh access needed. - These 3 files are world-readable system info; whitelisting them costs nothing security-wise versus the misleading fallback numbers.
- PHP-FPM caches
.user.iniforuser_ini.cache_ttl(default 300s), so the banner copy notes the 5-minute lag and the option to restart PHP-FPM for instant effect.
Graceful UX when sandboxed
- Blocked metrics now show "受沙箱限制" instead of
4 MB / 256 MB(which was actually PHP process RSS, not system RAM) or不可用 核(nullcpu_cores).
Other
- Logged-in nav order swap: 统计 now sits before 设置.
v2.3.2 — fix open_basedir warning on settings page
Patch release for hosts that restrict PHP via open_basedir (e.g. 宝塔面板 default).
What broke
The settings page surfaced a PHP warning the first time it tried to read uptime:
Warning: is_readable(): open_basedir restriction in effect. File(/proc/uptime) is not within the allowed path(s)
is_readable() is not silenced by @, so the probe leaked the warning even though the next line already wrapped file_get_contents() in @.
Fix
Removed the redundant is_readable() probe in get_server_uptime_seconds() (functions.php). @file_get_contents('/proc/uptime') already returns false silently when access is denied, and the existing shell_exec fallback still kicks in.
Settings page now renders without the warning. Uptime simply shows N/A on hosts where neither /proc/uptime nor shell_exec is reachable — expected behavior under hardened sandboxes.