{"id":4825,"date":"2026-08-14T13:18:06","date_gmt":"2026-08-14T13:18:06","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/08\/14\/reproducible-esp32-firmware-development-with-docker-and-docker-sandboxes\/"},"modified":"2026-08-14T13:18:06","modified_gmt":"2026-08-14T13:18:06","slug":"reproducible-esp32-firmware-development-with-docker-and-docker-sandboxes","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/08\/14\/reproducible-esp32-firmware-development-with-docker-and-docker-sandboxes\/","title":{"rendered":"Reproducible ESP32 Firmware Development with Docker and Docker Sandboxes"},"content":{"rendered":"<p class=\"wp-block-paragraph\">Firmware development has always been challenging: mismatched toolchains, \u201cit works on my machine\u201d builds, and the tension between maintaining legacy products and shipping new features. In this article we explore how you can use Docker and Docker sandboxes to ease firmware development, especially for ESP32 projects. Nowadays, teams end up supporting multiple hardware revisions, several ESP-IDF releases, and long-term customer deployments, all while iterating on new capabilities like Wi-Fi 6, Matter, or power optimizations.<\/p>\n<p class=\"wp-block-paragraph\">The official <code>espressif\/idf<\/code> Docker image solves the reproducibility problem. Docker Sandboxes (the <code>sbx<\/code> CLI) solve a newer one: letting AI coding agents work on your firmware at full speed without giving them the keys to your laptop. This article walks through a practical workflow that combines both: clean builds, parallel environments for new and legacy firmware, and safe unsupervised AI sessions.<\/p>\n<h2 class=\"wp-block-heading\">Part 1: The Baseline \u2013 Building with the Official Image<\/h2>\n<p class=\"wp-block-paragraph\">The <code>espressif\/idf<\/code> image ships a complete, pinned ESP-IDF installation: the framework itself, the Xtensa\/RISC-V toolchains, Python environment, CMake, ninja, everything. A build needs one command:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\ndocker run --rm -v $PWD:\/project -w \/project \n  -u $UID -e HOME=\/tmp \n  espressif\/idf:release-v5.4 idf.py build\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">A few details worth understanding rather than cargo-culting:<\/p>\n<ul class=\"wp-block-list\">\n<li><code>-u $UID -e HOME=\/tmp<\/code> makes the container run as your user, so build artifacts in <code>build\/<\/code> aren\u2019t owned by root. <code>HOME=\/tmp<\/code> gives the IDF tools a writable home for their caches.<\/li>\n<li><strong>Pin your tag.<\/strong> <code>latest<\/code> tracks the master branch and will break you eventually. <code>vX.Y<\/code> tags are fixed releases; <code>release-vX.Y<\/code> tags track the release branch and receive bugfixes. For products in maintenance, exact <code>vX.Y.Z<\/code> tags are the safest; for active development, <code>release-vX.Y<\/code> is a good balance.<\/li>\n<li>If your mounted project is owned by a different user than the one in the container, Git will complain about \u201cdubious ownership\u201d. The image supports <code>-e IDF_GIT_SAFE_DIR='\/project'<\/code> to whitelist the path (use : to separate multiple paths).<\/li>\n<li>Enable the compiler cache with <code>-e IDF_CCACHE_ENABLE=1<\/code> and persist it across runs by mounting a volume for it. Full rebuilds of a mid-size project drop from minutes to seconds.<\/li>\n<\/ul>\n<h3 class=\"wp-block-heading\">Flashing and monitoring<\/h3>\n<p class=\"wp-block-paragraph\">On <strong>Linux<\/strong>, pass the serial device through:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\ndocker run --rm -it \n  --device=\/dev\/ttyUSB0 \n  --group-add $(getent group dialout | cut -d: -f3) \n  -v $PWD:\/project -w \/project \n  -u $UID -e HOME=\/tmp \n  espressif\/idf:release-v5.4 idf.py flash monitor\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">The <code>--group-add<\/code> is needed because you\u2019re running as <code>$UID<\/code>, not root, and the device node belongs to <code>dialout<\/code>.<\/p>\n<p class=\"wp-block-paragraph\">On <strong>macOS and Windows<\/strong>, Docker Desktop cannot pass USB devices into containers. The clean workaround is a network serial bridge using RFC2217, which esptool supports natively. On the host:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\npip install esptool\nesp_rfc2217_server -p 4000 \/dev\/cu.usbserial-1420\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">Inside the container, point idf.py at the network port:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\nidf.py --port 'rfc2217:\/\/host.docker.internal:4000?ign_set_control' flash monitor\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">This looks like a hack but it\u2019s actually a feature: once the serial port is a network endpoint, <em>anything<\/em> can reach it. Containers, CI runners, and (as we\u2019ll see) sandboxed AI agents. Keep this trick in mind; it\u2019s the linchpin of Part 3.<\/p>\n<h3 class=\"wp-block-heading\">Hide it behind a Makefile<\/h3>\n<p class=\"wp-block-paragraph\">Nobody should type these commands twice. A small Makefile keeps the interface stable even if the plumbing changes:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\nIDF_IMAGE ?= espressif\/idf:release-v5.4\nPORT      ?= \/dev\/ttyUSB0\n\nDOCKER_RUN = docker run --rm -it \n  --device=$(PORT) \n  --group-add $(shell getent group dialout | cut -d: -f3) \n  -v $(PWD):\/project -w \/project \n  -v idf-ccache:\/ccache -e CCACHE_DIR=\/ccache -e IDF_CCACHE_ENABLE=1 \n  -u $(shell id -u) -e HOME=\/tmp -e IDF_GIT_SAFE_DIR=\/project \n  $(IDF_IMAGE)\n\nbuild:\n    $(DOCKER_RUN) idf.py build\n\nflash:\n    $(DOCKER_RUN) idf.py flash\n\nmonitor:\n    $(DOCKER_RUN) idf.py monitor\n\nmenuconfig:\n    $(DOCKER_RUN) idf.py menuconfig\n\nshell:\n    $(DOCKER_RUN) bash\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">Now <code>make build<\/code> works identically for every developer and in CI, and switching IDF versions is <code>make build IDF_IMAGE=espressif\/idf:release-v5.3<\/code>.<\/p>\n<h2 class=\"wp-block-heading\">Part 2: Parallel Environments \u2013 New Features and Legacy, Side by Side<\/h2>\n<p class=\"wp-block-paragraph\">This is where the container approach stops being merely convenient and starts changing how you work. Because each container is fully isolated, you can run two <em>different IDF versions<\/em> against two <em>different boards<\/em> at the same time, on the same machine.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\n# Terminal 1 - new feature branch, IDF 5.4, experimental board\ndocker run --rm -it --device=\/dev\/esp32-experimental \n  -v $PWD\/new-feature:\/project -w \/project \n  -u $UID -e HOME=\/tmp \n  espressif\/idf:release-v5.4\n\n# Terminal 2 - legacy firmware, IDF 5.3, production board\ndocker run --rm -it --device=\/dev\/esp32-production \n  -v $PWD\/legacy:\/project -w \/project \n  -u $UID -e HOME=\/tmp \n  espressif\/idf:release-v5.3\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">Typical uses: flashing experimental code on one board while a long-running soak test or customer demo stays untouched on the other; A\/B-comparing power consumption between firmware versions; reproducing a field bug on the exact legacy toolchain while the fix is developed on the current one.<\/p>\n<h3 class=\"wp-block-heading\">Stable device names with udev<\/h3>\n<p class=\"wp-block-paragraph\"><code>\/dev\/ttyUSB0<\/code> and <code>\/dev\/ttyUSB1<\/code> swap depending on plug order, which will eventually make you flash the wrong board. On Linux, pin them with udev rules keyed on the adapter\u2019s serial number:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\n# find the serial numbers\nudevadm info -a \/dev\/ttyUSB0 | grep '{serial}'\n# \/etc\/udev\/rules.d\/99-esp32.rules\nSUBSYSTEM==\"tty\", ATTRS{serial}==\"A50285BI\", SYMLINK+=\"esp32-experimental\"\nSUBSYSTEM==\"tty\", ATTRS{serial}==\"B7743NM0\", SYMLINK+=\"esp32-production\"\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">After <code>udevadm control --reload<\/code>, the symlinks survive reboots and re-plugs, and your Makefile targets can reference boards by role instead of by enumeration accident.<\/p>\n<h3 class=\"wp-block-heading\">Or codify it with Compose<\/h3>\n<p class=\"wp-block-paragraph\">If the two-environment setup is permanent, a <code>compose.yaml<\/code> documents it better than shell history:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\nservices:\n  new-feature:\n    image: espressif\/idf:release-v5.4\n    volumes: [\".\/new-feature:\/project\"]\n    working_dir: \/project\n    devices: [\"\/dev\/esp32-experimental:\/dev\/ttyUSB0\"]\n    stdin_open: true\n    tty: true\n\n  legacy:\n    image: espressif\/idf:release-v5.3\n    volumes: [\".\/legacy:\/project\"]\n    working_dir: \/project\n    devices: [\"\/dev\/esp32-production:\/dev\/ttyUSB0\"]\n    stdin_open: true\n    tty: true\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\"><code>docker compose run new-feature idf.py flash monitor<\/code> and the mapping from role to physical board is version-controlled.<\/p>\n<h2 class=\"wp-block-heading\">Part 3: Docker Sandboxes \u2013 Letting AI Agents Work Unsupervised<\/h2>\n<p class=\"wp-block-paragraph\">Coding agents like Claude Code are genuinely useful for firmware work: porting components between IDF versions, writing unit tests, chasing config drift in <code>sdkconfig<\/code>. But to be useful they need to <em>run things<\/em>: builds, flashes, <code>pip install<\/code>, sometimes Docker itself. Giving an agent that freedom directly on your host, in bypass-permissions mode, is uncomfortable for good reasons.<\/p>\n<p class=\"wp-block-paragraph\">Docker Sandboxes solve this with a stronger primitive than a container: each sandbox is a <strong>microVM with its own kernel, filesystem, network stack, and its own private Docker daemon<\/strong>. The agent can install packages, modify system config, build and run containers, and none of it touches your host. Your workspace directory syncs into the sandbox at the same path, so file paths in error messages match between the two worlds.<\/p>\n<p class=\"wp-block-paragraph\">The CLI is small and clear:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\n# start Claude Code in a sandbox for the current project\nsbx run claude\n\n# work on a specific directory\nsbx run claude ~\/firmware\/new-feature\n\n# see what's running, resource usage, network requests\nsbx\n\n# list and clean up\nsbx ls\nsbx rm new-feature\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">Three properties matter for firmware work in particular:<\/p>\n<ol class=\"wp-block-list\">\n<li><strong>Disposability.<\/strong> The agent can trash its environment experimenting with esptool versions, partition tables, or custom toolchains. <code>sbx rm<\/code> and it never happened. Your host IDF setup, if you even have one, is untouched.<\/li>\n<li><strong>Network policy.<\/strong> Sandboxes route traffic through a host-side proxy with three modes: <em>open<\/em>, <em>balanced<\/em> (default-deny with pre-approved developer and package-manager domains), and <em>locked down<\/em>. An agent that decides to <code>curl<\/code> your firmware to somewhere unexpected simply can\u2019t.<\/li>\n<li><strong>Credential isolation.<\/strong> API keys and tokens are injected by the host-side proxy into outgoing requests; the sandbox itself never sees them. A prompt-injected agent can\u2019t exfiltrate what it doesn\u2019t have.<\/li>\n<\/ol>\n<h3 class=\"wp-block-heading\">But how does the agent flash a board?<\/h3>\n<p class=\"wp-block-paragraph\">Here\u2019s where the RFC2217 trick from Part 1 pays off. The sandbox is a VM; there is no USB passthrough. But there <em>is<\/em> a network path to the host. So expose the serial port as a network service on the host:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\nesp_rfc2217_server -p 4000 \/dev\/esp32-experimental\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">and tell the agent (in your project\u2019s CLAUDE.md or equivalent) to flash with:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\nidf.py --port 'rfc2217:\/\/host.docker.internal:4000?ign_set_control' flash monitor\n\n<\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">Now the agent\u2019s whole loop runs end-to-end inside the sandbox: edit, build in a container it spawned itself, flash real hardware, read the monitor output, fix the bug. The only thing it can reach on your machine is one serial port you explicitly published. That\u2019s a remarkably good trade: full hardware-in-the-loop autonomy, minimal blast radius.<\/p>\n<p class=\"wp-block-paragraph\">Run one sandbox per board and you get the parallel-environment pattern from Part 2, agent edition: an agent iterating on the experimental board via port 4000 while you, or a second locked-down agent, watch the production board via port 4001.<\/p>\n<h3 class=\"wp-block-heading\">Honest caveats<\/h3>\n<p class=\"wp-block-paragraph\">Sandboxes are newer technology than containers, and it shows in places. MicroVM isolation is available on macOS (Apple Silicon), Windows 11, and Linux with KVM. Build performance inside the microVM is noticeably slower than native containers: fine for agent sessions, annoying for your own tight inner loop. And the agent runs in bypass-permissions mode by design; the isolation <em>is<\/em> the permission system, so review the diff before merging, same as you would for any contributor.<\/p>\n<h2 class=\"wp-block-heading\">Part 4: Putting It Together \u2013 A Daily Workflow<\/h2>\n<ul class=\"wp-block-list\">\n<li><strong>Regular development:<\/strong> VS Code Dev Containers with the <code>espressif\/idf<\/code> image (plus the Espressif IDF extension inside the container). Same image as CI, full IntelliSense, native-container speed.<\/li>\n<li><strong>AI-assisted experimentation:<\/strong> <code>sbx run claude --branch &lt;feature&gt;<\/code>. The branch flag keeps the agent\u2019s commits on a worktree, so your checkout stays clean; review and merge when it\u2019s done.<\/li>\n<li><strong>Multi-board testing:<\/strong> parallel containers (you) or parallel sandboxes (agents), one per device, with udev-stable names and one <code>esp_rfc2217_server<\/code> per board.<\/li>\n<li><strong>CI:<\/strong> GitHub Actions with the official <code>espressif\/esp-idf-ci-action<\/code>, pinned to the same IDF version as your dev image. If a build passes locally, it passes in CI. It\u2019s the same bits.<\/li>\n<\/ul>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\">\n# .github\/workflows\/build.yml\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with: { submodules: recursive }\n      - uses: espressif\/esp-idf-ci-action@v1\n        with:\n          esp_idf_version: v5.4\n          target: esp32s3\n\n<\/pre>\n<\/div>\n<h2 class=\"wp-block-heading\">Pro Tips<\/h2>\n<ul class=\"wp-block-list\">\n<li>Pin exact image tags (<code>release-v5.4<\/code>, not <code>latest<\/code>), and record the tag in the repo (Makefile or compose file) so the toolchain version is part of the code review.<\/li>\n<li>One project folder per product line (<code>new-feature\/<\/code>, <code>legacy\/<\/code>) with its own pinned image. Never share a <code>build\/<\/code> directory between IDF versions.<\/li>\n<li><code>IDF_GIT_SAFE_DIR=\/project<\/code> kills the Git ownership warnings; <code>IDF_CCACHE_ENABLE=1<\/code> plus a ccache volume kills the rebuild times.<\/li>\n<li>Add <code>--group-add<\/code> for the dialout GID when combining <code>--device<\/code> with <code>-u $UID<\/code>.<\/li>\n<li>On macOS\/Windows, and always with sandboxes, RFC2217 is your serial transport. One server per board, one port per server.<\/li>\n<li>Put the flash\/monitor commands and port mapping in <code>CLAUDE.md<\/code> so agents discover the hardware setup without being told each session.<\/li>\n<li>If your team standardizes on extra tools (clang-tidy, cppcheck, a particular esptool), bake a thin custom image <code>FROM espressif\/idf:release-v5.4<\/code> rather than installing them in every session.<\/li>\n<\/ul>\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n<p class=\"wp-block-paragraph\">Docker turned ESP32 builds from a fragile, machine-specific ritual into something reproducible enough to trust. Parallel containers turn one desk into a small hardware lab, with legacy and next-gen firmware coexisting without friction. And Docker Sandboxes close the last gap: they make it reasonable, not reckless, to hand an AI agent a real board and let it work.<\/p>\n<p class=\"wp-block-paragraph\">If you\u2019re still installing ESP-IDF directly on your host machine in 2026, you\u2019re working harder than necessary. Try the two-board setup this week: new firmware iterating on one device, stable firmware soaking on the other. Then hand one of them to an agent in a sandbox and see how far it gets.<\/p>\n<p class=\"wp-block-paragraph\">Happy hacking!<\/p>\n<h3 class=\"wp-block-heading\">Learn more<\/h3>\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/stable\/esp32\/api-guides\/tools\/idf-docker-image.html\" rel=\"nofollow noopener\" target=\"_blank\">ESP-IDF Docker image guide<\/a><\/li>\n<li>Review the <a href=\"https:\/\/docs.docker.com\/ai\/sandboxes\/\" rel=\"nofollow noopener\" target=\"_blank\">Docker Sandboxes<\/a> documentation<\/li>\n<li>Read the <a href=\"https:\/\/www.docker.com\/blog\/docker-sandboxes-run-claude-code-and-other-coding-agents-unsupervised-but-safely\/\">Docker blog about how to\u00a0 run Claude Code and other coding agents safely<\/a><\/li>\n<li>Review the GitHub action for building ESP-IDF projects: <a href=\"https:\/\/github.com\/espressif\/esp-idf-ci-action\" rel=\"nofollow noopener\" target=\"_blank\">esp-idf-ci-action<\/a><\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">\n<\/p>","protected":false},"excerpt":{"rendered":"<p>Firmware development has always been challenging: mismatched toolchains, \u201cit works on my machine\u201d builds, and the tension between maintaining legacy [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":94,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[4],"tags":[],"class_list":["post-4825","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-docker"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4825","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=4825"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4825\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/94"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=4825"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=4825"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=4825"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}