본문 바로가기
C.W.K.
Stream
Lesson 01 of 10 · published

Anchor 1 — Manifest 해부

~10 min · manifest, case-study, chromeembed, v0.2.1

Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"ChromeEmbed v0.1이 manifest 28 줄과 script file 넷. Lesson 1이 manifest — 모든 field 정당화, 모든 선택이 이전 track 중 하나로 묶임."
동작 중인 Pippa ChromeEmbed v0.1 — Chrome window 오른쪽 가장자리에 docked 된 Pippa chat panel, 메인 뷰포트에 host page 보임
Pippa ChromeEmbed v0.1 — 이 Track 9가 file by file walk 하는 working prototype.

28 줄

실제 ChromeEmbed manifest, end to end:

{
  "manifest_version": 3,
  "name": "Pippa ChromeEmbed",
  "version": "0.1.0",
  "description": "The household, present on the current page.",
  "permissions": ["sidePanel", "activeTab", "storage", "tabs", "scripting"],
  "host_permissions": [
    "<all_urls>",
    "http://localhost:5173/*",
    "http://127.0.0.1:5173/*",
    "http://100.x.x.x:5173/*"
  ],
  "background": { "service_worker": "background.js" },
  "side_panel": { "default_path": "sidepanel.html" },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content-script.js"],
    "run_at": "document_idle",
    "all_frames": true
  }],
  "action": { "default_popup": "popup.html" },
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'; frame-src http://localhost:5173 http://127.0.0.1:5173 http://100.x.x.x:5173;"
  }
}

Permission — 각자 이유

  • sidePanel — chrome.sidePanel.* (Track 4) 위해 필수.
  • activeTab — user-gesture-trigger 된 scripting 호출 cover, SW가 standing host access 없이 현재 page의 selection read (Track 6 Lesson 4).
  • storage — 나중을 보고 적어 뒀어. v0.1은 SW 메모리의 Map 으로 버티고 있고. 그냥 두는 값은 거의 없으니까.
  • tabs — background.js가 activeTab query 결과에서 tab.url, tab.title, tab.favIconUrl read 해서 필요 (moderate 'browsing history' install 경고).
  • scripting — background.js가 content script가 report 안 했을 때 selection read fallback 으로 chrome.scripting.executeScript 호출해서 필요 (all-frames 경로).

Host Permission — Localhost set

두 layer가 double duty:

  • host_permissions<all_urls>content_scripts.matches — 둘 다 필요해. ChromeEmbed는 정해진 사이트 몇 곳이 아니라 user가 가는 어느 페이지에서든 맥락을 건네주려고 하거든.
  • Localhost와 127.0.0.1 — development 위해. cwkPippa dev server가 localhost:5173에서 돔. side panel iframe이 거기서 load.
  • 100.x.x.x (Tailscale의 CGNAT 대역이야 — 각자 자기 사설 VPN IP로 바꿔) — 같은 사설망에 있는 다른 기기에서도 dev server 에 닿게 해 줘. ChromeEmbed는 집 안의 Mac 어디서나 돌거든. 그때그때 cwkPippa를 띄워 둔 Mac의 Tailscale 주소로 찾아가면 돼.

이 localhost / Tailscale URL이 두 자리에 나타나는 거 주목: manifest의 host_permissions 배열 (network access 위해) AND extension_pages CSP의 frame-src directive (iframe specifically 위해). 어느 쪽에서든 제거하면 panel 깨짐. Track 4 Lesson 5가 CSP detail walk.

all_frames 선택

content script는 all_frames: true로 걸어 뒀어. user가 iframe 안에서 뭘 긁어도 — 끼워 넣은 YouTube 댓글이든 Notion embed 든 — 선택 영역과 화면 텍스트를 같이 담고 싶거든. 기본값인 all_frames: false로 두면 그런 건 다 놓쳐. 대신 치를 값이 있어. 모든 iframe이 content script를 올리게, 메모리 비용. Pippa-grade household extension 엔 받아들일 만.

action.default_popup 선택

Popup 존재 (popup.html 선언) 하지만 6-줄 doorway (Lesson 4). action.default_popup: "popup.html" 패턴이 popup 자체 chrome.sidePanel.open() + window.close()와 함께 의미: toolbar icon click → popup 잠깐 flash → side panel 열림 → popup 닫힘. User가 한 fluid 'click이 panel 엶' 동작 봄.

각 결정 만든 Track

  • Track 1 — manifest_version 3, name, version, 기본.
  • Track 2 — service_worker 선언.
  • Track 3 — all_frames 가진 content_scripts.
  • Track 4 — sidePanel permission + side_panel.default_path + extension_pages CSP frame-src.
  • Track 5 — action.default_popup.
  • Track 6 — narrow permission set (downloads 없음, notifications 없음). storage permission이 overhead 지만 v0.2 위해 예약.

이 중 novel 한 거 없음. 첫 여덟 track이 one working file로 composing 된 것.

Manifest가 계약. 모든 field가 feature로 정당화, 모든 permission이 feature가 허용하는 만큼 narrow. ChromeEmbed의 28 줄이 여덟 track concrete 된 것.
왜 1.0.0 아닌 0.1.0? ChromeEmbed가 자기 v0.1 이름 일부러 — larger framework story (PIPPA-EMBEDS.md) 의 첫 concrete embed surface. v1이 downstream embed (Adobe, Mail, Calendar) 가 상속할 API surface 표시. ChromeEmbed가 shape 증명하고 그 계약 calcify 전 gap 노출.

Code

embeds/chrome/manifest.json — 실제 ChromeEmbed v0.1 manifest·json
{
  "manifest_version": 3,
  "name": "Pippa ChromeEmbed",
  "version": "0.1.0",
  "description": "The household, present on the current page.",
  "permissions": ["sidePanel", "activeTab", "storage", "tabs", "scripting"],
  "host_permissions": [
    "<all_urls>",
    "http://localhost:5173/*",
    "http://127.0.0.1:5173/*",
    "http://100.x.x.x:5173/*"
  ],
  "background": { "service_worker": "background.js" },
  "side_panel": { "default_path": "sidepanel.html" },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content-script.js"],
    "run_at": "document_idle",
    "all_frames": true
  }],
  "action": { "default_popup": "popup.html" },
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'; frame-src http://localhost:5173 http://127.0.0.1:5173 http://100.x.x.x:5173;"
  }
}
v0.2.1 checkpoint — embeds/chrome/manifest.json — 현재 v0.2.1 shape abridged·json
{
  "manifest_version": 3,
  "name": "Pippa ChromeEmbed",
  "version": "0.2.1",
  "permissions": ["sidePanel", "activeTab", "storage", "tabs", "scripting", "clipboardWrite"],
  "host_permissions": [
    "<all_urls>",
    "http://localhost:5173/*",
    "http://127.0.0.1:5173/*",
    "http://100.x.x.x:5173/*"
  ],
  "background": { "service_worker": "background.js" },
  "side_panel": { "default_path": "sidepanel.html" },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "exclude_matches": [
      "http://localhost:5173/embed/*",
      "http://127.0.0.1:5173/embed/*",
      "http://100.x.x.x:5173/embed/*"
    ],
    "js": ["pippa-hosts.js", "content-script.js"],
    "run_at": "document_idle",
    "all_frames": true
  }],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'; connect-src http://localhost:5173 http://127.0.0.1:5173 http://100.x.x.x:5173; frame-src http://localhost:5173 http://127.0.0.1:5173 http://100.x.x.x:5173;"
  }
}

External links

Exercise

실제 cwkPippa/embeds/chrome/manifest.json과 이 abridged block을 field별로 맞춰 봐. pippa-hosts.js의 frontend origin 하나가 host_permissions, exclude_matches, CSP 두 directive에 어떻게 반복되는지 따라가. 버리는 copy에서 connect-src origin 하나를 빼서 frontend probe가 실패하는 걸 확인한 다음, 실제 extension reload 전에는 원복해.
Hint
Host 변경은 JavaScript와 manifest 세 surface가 모두 맞아야 끝이야. frame-src는 iframe load, connect-src는 reachability probe, exclude_matches는 self-capture 방지를 맡아.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.