Web Device recognizes a narrow set of standalone Markdown links as content embeds. This is not a general permission to frame arbitrary sites. The current content reader recognizes YouTube video URLs, X/Twitter status URLs, and validated same-site absolute paths. Other URLs remain visible links.
This page is a live feature guide. Atomic directive contract: embed unit. Use that page for the single-unit matrix row; use this page for how-to plus working frames.
Use an explicit embed directive when the source itself is part of the documentation. It makes the authoring intent unambiguous and puts the source directly above the result.
Sample contract
| Field | Value |
|---|---|
| Purpose | Supported third-party and same-site embeds in content Markdown |
| Boundaries | Not a general iframe capability; document stays read-only for site content/AUP state |
| Author entry | ::embed{url=…} or standalone recognized URL |
| Surface | Web Device document → frame/media with provider-specific sandbox |
| Public status | released for YouTube, X status, validated internal paths |
| Fallback / failure | Unrecognized or refused URL → ordinary link / no embed descriptor |
| Verification | Routes below · arc --version = 2.0.0-beta.32 · same-site host only for internal paths |
A YouTube embed
Source:
::embed{url="https://youtu.be/jNQXAC9IVRw"}Rendered:
The current renderer turns this supported URL into a responsive, lazy-loaded, sandboxed third-party iframe. The document stays read-only: the frame is not an editor for this Markdown item or an AUP session owned by the document. Read-only refers to this site's content and AUP state, not to network behavior inside the third-party frame.
An X post
Source:
::embed{url="https://x.com/arcblock_io/status/1955367769672950142"}Rendered:
The renderer keeps the original status URL as the frame fallback target. The availability and appearance of the embedded post remain controlled by X; do not treat a third-party frame as evidence that a document has persisted state or granted a capability.
A same-site page or deck
Use one site-absolute path, beginning with a single /, to embed a page from the same site. After the client-side slides widget initializes, a slides record uses this form as a 16 player surface; before then, its server-rendered transcript remains part of the frame. Opening the source route directly keeps the normal readable transcript.
Source:
::embed{url="/en/docs/web-device/slides-demo/"}Rendered:
The path must remain on this site. Protocol-relative paths, traversal segments, backslashes, and control characters are refused; this directive does not grant a general external iframe capability. A query and fragment can select the target page's supported state, such as a deck's ?full#slide-… link. See Markdown slides for the deck contract.
Same-site embeds load the target page inside an iframe. ARC’s default frame-ancestors 'none' blocks that framing. Sites that embed their own pages must set .web/security:
frame-ancestors: "'self'"Without it, the iframe is blank while the target URL still works when opened alone. Deck and other internal embeds on this site depend on that override.
Refusal and ordinary-link fallback
resolveContentEmbed returns no descriptor when the URL is empty, not a recognized provider, fails URL parsing, uses a non-http(s) scheme, or fails the internal-path checks. In content Markdown those cases stay ordinary links (or bare text), not frames.
| Input | Expected result |
|---|---|
| Supported YouTube / X status HTTPS URL | Provider embed frame |
Site path /en/docs/…/ without .., \, or control chars | Same-site internal embed |
//evil.example/ (protocol-relative) | Refused as internal path → no embed |
/../secret or path segments containing .. | Refused (traversal) |
Path with control characters or \ | Refused |
https://example.com/not-a-provider | No resolver match → ordinary link |
| Inline link inside a sentence | Always stays a normal link |
Do not paste a rejected path into a live ::embed on this page just to “prove” failure: the failure mode is “no embed,” not a special error chrome. Keep refusal cases in the source fence above, not as live frames.
Keep surrounding prose as a link
Only a standalone supported URL or an explicit directive becomes an embed. A link inside a sentence remains a normal link:
Read [the original post](https://x.com/arcblock_io/status/1955367769672950142) before continuing.Rendered:
Read the original post before continuing.
For the exact content-item and locale structure, see Content objects, metadata, and locales. This page documents recognized external providers and validated same-site paths; verify the security and trust boundary of a general frame against its target renderer before using one.
Verification evidence
| Item | Value |
|---|---|
| Baseline | 2026-08-12 · arc --version → 2.0.0-beta.32 |
| Capability row | docs/aup/capability-matrix.md (internal, non-routed) · ::embed |
| Implementation | providers/runtime/web-device/src/content-embed.ts (resolveContentEmbed, checkInternalEmbedPath); content-reader.ts embed case |
| Site framing | .web/security → frame-ancestors: "'self'" for same-site iframes |
| Unit page | embed |
| Public routes | /en/docs/web-device/markdown-embeds/ · /zh/docs/web-device/markdown-embeds/ |
| Local run | arc service start --blocklet blocklets/arcblock --port 4900 · confirm YouTube + same-site frames; X may depend on third-party availability |
| Accept | Supported URLs frame (not blank); unsupported stay links; internal path stays same-site only |