diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
@@ -0,0 +1,41 @@
+name: Deploy to GitHub Pages
+
+on:
+ push:
+ branches: [main]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# 동시에 두 배포가 겹치지 않게. 진행 중인 배포는 끊지 않는다.
+concurrency:
+ group: pages
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - uses: withastro/action@v6
+ with:
+ node-version: 24
+ # npm run build 로 돌려야 prebuild(콘텐츠 게이트)가 함께 실행된다.
+ # astro build 를 직접 부르면 게이트가 건너뛰어진다.
+ build-cmd: npm run build
+ env:
+ # 백필이 끝나면 all 로 올리고 **다시 내리지 않는다**.
+ CONTENT_STRICT: urls,schema
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - id: deployment
+ uses: actions/deploy-pages@v5
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,5 @@
+node_modules/
+dist/
+.astro/
+public/build-info.json
+.DS_Store
diff --git a/README.md b/README.md
@@ -0,0 +1,49 @@
+# blog.sqzer.com
+
+개인 연구 노트. Astro 로 짓고 GitHub Actions 가 GitHub Pages 에 배포한다.
+
+## 글 쓰기
+
+```bash
+cp content/_template.md content/writing/my-post.md # 또는 직접 생성
+npm run dev
+```
+
+`content/writing/<slug>.md` 하나가 글 하나다. 파일명이 곧 URL 슬러그이고,
+발행 주소는 front matter 의 `date` 에서 연도를 따 **`/writing/<year>/<slug>/`** 가 된다.
+
+```yaml
+---
+title: "" # 필수
+titleKo: "" # 한글 원제 보존용, 선택
+deck: "" # 인덱스에 제목과 함께 나오는 한 문장. 비어 있어도 빌드는 통과한다
+date: 2026-09-17 # 필수. YYYY-MM-DD
+type: essay # essay | research — 하위 메뉴가 이 값으로 갈린다
+tags: []
+draft: true # true 면 빌드에서 제외된다
+---
+```
+
+초안은 **로컬에 두고 완성됐을 때 push** 한다. 이 저장소는 공개다.
+
+이미지는 `public/uploads/` 에 두고 본문에서 `/uploads/...` 로 참조한다.
+
+## 배포
+
+`main` 에 push 하면 Actions 가 빌드해 Pages 로 올린다. 빌드 앞에 콘텐츠 게이트가 걸려 있어
+(`npm run prebuild`) 스키마 위반·URL 충돌·없는 이미지 참조가 있으면 **배포되지 않는다.**
+
+```bash
+npm run check # 게이트만 따로 돌리기
+npm run build # 게이트 + 빌드
+```
+
+게이트 엄격도는 워크플로의 `CONTENT_STRICT` 로 조절한다. 지금은 `urls,schema` 이고,
+덱·태그 백필이 끝나면 `all` 로 올린 뒤 다시 내리지 않는다.
+
+## 설계 메모
+
+- 정식 URL 은 전부 트레일링 슬래시로 끝난다
+- URL 슬러그는 **ASCII 만** 소문자화한다(한글 파일명은 그대로 살린다)
+- 목록 정렬은 `date DESC → Intl.Collator('en') title → path` — DB·파일시스템 순서에 맡기지 않는다
+- 연도를 URL 에 넣는 이유는 `essay`·`research` 같은 하위 뷰 이름과 슬러그가 충돌하지 않게 하기 위해서다
diff --git a/astro.config.mjs b/astro.config.mjs
@@ -0,0 +1,11 @@
+// @ts-check
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ site: 'https://blog.sqzer.com',
+ output: 'static',
+ // 정식 URL 은 전부 트레일링 슬래시로 끝난다. directory 포맷이 dist/<path>/index.html 을 만든다.
+ // ⚠️ 프리렌더 페이지의 슬래시는 Astro 가 강제하지 않는다 — 실제 집행자는 vercel.json 이다.
+ trailingSlash: 'always',
+ build: { format: 'directory' },
+});
diff --git a/content/_template.md b/content/_template.md
@@ -0,0 +1,11 @@
+---
+title: ""
+titleKo: ""
+deck: ""
+date: 2026-01-01
+type: essay
+tags: []
+draft: true
+---
+
+본문은 여기부터.
diff --git a/content/about.md b/content/about.md
@@ -0,0 +1,15 @@
+---
+title: "whoami"
+date: 2024-10-31
+---
+
+# whoami
+분석하고 본질 탐구하는거 좋아함
+
+# Contact
+Email: contact.extinct932@passmail.net
+
+# Link
+- Github: https://github.com/devastator-x
+- Service: https://lacrymaria.com
+- LinkedIn: https://www.linkedin.com/in/devastator-x
\ No newline at end of file
diff --git a/content/writing/AIOdyssey.md b/content/writing/AIOdyssey.md
@@ -0,0 +1,128 @@
+---
+title: "AI Odyssey"
+date: 2026-08-13
+tags: []
+---
+
+
+
+# EN
+
+I'm going to write down my experiences and conclusions after using AI to the point where I practically became one with it.
+
+Let me be clear right off the bat: I'm not trying to debate right or wrong here. These are purely my own thoughts.
+
+After pushing AI to its limits for nearly four years, my conclusion is that AI is a bubble. Or, to put it more simply, it’s like a cringe-worthy middle-aged guy trying desperately to act like a Gen Z-er. No matter how hard he tries to mimic them, he can never truly pull it off—it’s a futile attempt.
+It just looks like everyone is either hopping on the AI bubble or chasing a pipe dream to their graves.
+
+I honestly don't even want to associate with people who throw around buzzwords like E2E, AX, or "vibe coding" when they haven't even spent sleepless months grinding with AI to actually produce tangible results.
+
+Those people fall into one of two categories: either they’ve never built a real commercial service (not just a personal side project) using AI, or they have built one, and they are somehow satisfied with its mediocre quality. I firmly believe there are no other types besides these two.
+
+I despise both, but if someone is actually satisfied with a commercial service built purely with AI, they are the more pathetic ones. It just shows how painfully low their standards are.
+
+Anyway, moving forward, I’m canceling all my AI subscriptions, except for Gemini for research purposes. I’ll just write off the hefty fees as an expensive tuition.
+
+Unless a true AGI drops, I’m no longer going to be duped by overblown marketing into wasting my time and money.
+People might ask, "Then how do you explain AI providers solving unsolved math problems or finding zero-day vulnerabilities?" Let me tell you: I sincerely hope nobody is naive enough to think that the massive LLMs these companies run internally, using all their massive computational assets, are the exact same commercial models you get to use.
+
+Most decision-makers at Big Tech companies (I won't name names, but they all basically parrot the same rhetoric with slight variations) talk about the shift to AI not as a coexistence with traditional computer science, but as a total replacement.
+They claim that only those who abandon traditional computing logic and mindsets and learn to orchestrate and build AI agents will survive.
+I am highly skeptical of this narrative.
+
+First of all, "probability" and "capability" are two very different things. An 80% chance of being able to do something is absolutely not the same as actually being able to do it. Just because something can be partially replaced doesn't mean it's a complete, flawless replacement.
+AI and humans are fundamentally different. They might seem similar, but they are absolutely not the same.
+Given the inherent bottleneck between how AI approaches problem-solving and how humans do, I seriously question how anyone thinks true collaboration between the two is even possible.
+At the end of the day, one is just holding the other back from its objective. A human orchestrating an AI agent might actually be throttling the AI's potential, or conversely, the direction and execution of the AI agent might just be dragging the human down.
+
+Secondly, I consider the "skill" of building and orchestrating AI agents to be on par with, or even lower than, the skill of being good at Googling.
+We first need to define whether AI is actually a "tool." If it’s a tool, an identical input must yield an identical output. The moment a result is randomized, it loses its qualification as a reliable tool.
+Furthermore, a true tool operates within a strictly defined scope, but AI completely defies this definition as well. For these reasons, it's very hard to classify AI as a proper tool.
+Think about using a knife or a bow. When you use a knife, you stab or cut. The exact outcome might vary slightly depending on the user's skill, but the fundamental result—swinging it to stab or cut—is perfectly consistent.
+The same goes for a bow. You draw the string and shoot an arrow to hit a target. The arrow doesn't magically boomerang back at you, shoot straight up into the sky, or randomly nosedive into the dirt.
+
+In contrast, the "skill" of utilizing AI agents produces wildly different outcomes depending on the time, environment, user, and budget.
+Honing your "AI skills" merely increases the probability of getting a better outcome; it never guarantees a consistently identical one. By my standards, I simply cannot accept a technique with such wildly inconsistent results as a legitimate technical skill.
+
+You are entirely dependent on the performance of commercial AI agent models. And don't even get me started on all the "Skills," "Plugins," and "MCPs"—it’s an endless list of things to learn that constantly change on a whim.
+Anyway, the ROI (Return on Investment) is garbage, and if anything, using it lowers my own standards; it absolutely does not make me smarter.
+To make matters worse, the "knowledge" you gain through AI isn't even something you can own. It's totally volatile—if the AI model disappears, that knowledge vanishes with it.
+
+Some might argue, "What about local LLMs?" Honestly, that just makes me laugh. How on earth do you expect to beat the massive infrastructure backing commercial LLMs? The sheer gap in computing power is insurmountable.
+Instead of burning cash trying to build a local LLM rig, you’d be much better off just buying semiconductor stocks with that money.
+
+Tinkering with LLMs is the equivalent of a senior design project in college. The mere thought of proudly flaunting that as a professional achievement in the real world gives me second-hand embarrassment.
+
+I don't consider any technology that lacks a solid foundation and can't even survive for five years to be actual "tech," which is why I absolutely do not view AI prompting/wrangling as a real technical skill.
+
+Anyway, ever since the AI boom, there's been an exhausting surge of people who just talk a big game without putting in the work. It honestly feels like we’ve mistakenly handed a dangerous weapon called AI to foolish people.
+In the past, when you had to deal with an idiot, you could just shut them down with basic facts and logic. Now, it's exhausting because you have to argue with AI users heavily armed with confidently incorrect information and pseudo-logic.
+
+Conclusion: AI is a fun toy. Do not take it seriously. Of course, the financial market and actual technological value have zero correlation, so feel free to keep buying AI-related stocks with peace of mind. Keep your heart cold, and your head hot.
+
+
+-------------------------------
+# KR
+그동안 AI와 거의 물아일체 수준으로 사용해본 경험과 내 결론을 적어보겠다.
+
+먼저 옳고 그름을 따지려는게 아닌 내 생각임을 분명히 하겠다.
+
+여태까지 AI를 거의 4년가까이 굴려본 결과 AI는 거품이다. 아니, 더 쉽게 표현하자면 2030 따라하는 영포티같은 존재다. 영포티가 아무리 2030을 따라하려고 했도 닿을 수 없는것처럼 신포도와 같다.
+그냥 모두가 AI라는 거품에 올라타거나 허왕된 꿈을 죽을때까지 쫒아가는 그림이다.
+
+진짜 잠도 안자고 몇달내내 AI굴려서 결과 내보지도 않은 사람들이 E2E, AX, 바이브코딩 이러는거 보면 상종하기도 싫다.
+
+그 사람들은 둘중 하나이다. AI로 개인 프로젝트가 아닌 상용 서비스를 만들어본적이 없는 사람이던가.
+아니면 AI로 상용서비스를 만들었는데 그 퀄리티에 만족하는 사람이다.
+딱 이 두분류 말고 없다고 생각한다.
+
+둘다 극혐하지만 만약 AI로 만든 상용서비스가 본인의 만족감을 채워준다면 그게 더 한심한 놈이다. 그릇이 거기까지 밖에 안되는거.
+
+아무튼 이제부터 AI는 리서치용 Gemini를 제외하고 구독을 끊을 예정이다.
+비싼돈 수업료로 냈다 생각해야지.
+
+AGI급 나오지 않는이상 허황된 마케팅에 속아넘어가 시간과 비용을 지불하지 않을것이다.
+그럼 AI프로바이더서 수학난제 풀고 제로데이 취약점 찾고 그런건 어떻게 설명할꺼냐 그러는데 AI회사에서 사용가능한 모든 자산을 활용한 LLM과 본인이 사용하고 있는 상용모델이 같을꺼라 생각하는 사람이 없길 바란다.
+
+대부분의 빅테크 기업들의 의사결정권자들(누군지는 특정하지 않겠다. 대부분이 비슷한 맥락의 말을 조금씩 바꿔서 말할뿐 기조는 같다고 본다) 은 AI로 전환이 기존 컴퓨터 과학과의 공존이 아닌 완전한 전환으로 이야기 하고 있다.
+기존의 컴퓨터 논리구조, 사고방식을 벗어나서 AI 에이전트를 오케스트레이션 하고 빌드할 줄 아는 사람만이 살아남을 수 있다고 한다.
+나는 이 이야기에 회의적이다.
+
+먼저 가능성과 가능은 다르다.
+가능성 80% 와 가능은 절대 다르다.
+어느정도 대체가 가능하다는 점은 완벽히 가능한게 아니다.
+AI와 인간은 절대 다르다. 비슷한거지 같은게 아니다.
+그런데 AI가 문제를 해결하는 방식과 사람이 문제를 해결하는 방식사이의 병목현상이 존재한 상태에서
+어떻게 AI와 사람이 협업이 가능하다고 생각하는지 의문이다.
+누가되었든 둘중 하나는 서로의 목적에 발목을 잡고 있는 상태일 뿐이다.
+사람이 AI 에이전트를 오케스트레이션 한다는것 자체가 AI의 발목을 잡고 있는걸 수도 있고
+AI 에이전트가 수행하는 작업과 방향성이 사람의 발목을 잡고 있는걸 수도 있다.
+
+두번째로 AI에이전트를 활용한 빌드및 오케스트레이션 기술은 구글링을 얼마나 잘하는지와 동일하거나 더 낮은 기술이라 생각한다.
+AI를 도구로 볼 것인가 부터 정의해야 한다.
+AI가 도구이면 동일한 입력값에는 동일한 출력값이 나와야 한다. 결과값이 랜덤하게 나오는순간 그건 도구라 보기 어렵다.
+또한 AI가 도구라면 정해진 범주 내에서 동작해야 한다. 하지만 이것마저 도구라는 정의에서 벗어난다.
+해당 이유로 역시 AI는 도구라 보기 힘들다.
+우리가 칼을 사용하거나 활을 사용할때 칼로 무언가를 찌르고 자른다. 이것은 도구를 쥔 인간의 역량에 따라 조금씩의 차이는 있지만 칼을 휘둘러서 찌르고 밴다라는 결과값이 나오는건 항상 일치한다.
+활도 마찬가지로 활을 당겨서 화살을 쏴 어딘가에 맞춘다. 자신에게 화살이 돌아오거나 앞으로 쏜 화살이 하늘로 올라가거나 갑자기 땅으로 박히지는 않는다.
+
+즉, AI 에이전트 활용 기술이라 함은 시간과 공간, 사용자, 비용에 따라 결과값이 천차만별로 달라진다.
+AI 스킬을 연마할 수록 결과값이 더 나아질 확율이 높아지는거지 항상 같은 결과값을 내는것은 아니다.
+결과가 일정하지도 않은 기술은 내 기준으로서 도저히 기술이라 받아들이기 힘들다.
+
+상용 AI Agent 모델 성능에 종속되어 있고 뭔놈의 Skill, Plugin, MCP는 배워야 할것도 많고 휙휙 바뀌고.
+아무튼 투자대비 효율성도 떨어지고 내가 수준 낮아졌으면 낮아졌지 절대 내가 똑똑해지지 않음.
+심지어 AI로 얻는 지식은 소유할 수 있는것도 아니고 AI모델이 사라지면 없어지는 휘발성임.
+로컬LLM이 있지않냐? 웃음만 나옴. 상용 LLM모델이 돌아가고 있는 인프라를 어떻게 이김?
+연산수준만 봐도 넘사인데.
+로컬LLM 구축할바에 그냥 그돈으로 반도체시장 주식이나 사시길.
+
+LLM모델 사용해보는건 대학교 졸작 수준이지 절대 이걸 밖에 자랑스럽게 내놓고 다닌다는건 상상만으로도 부끄러움.
+
+난 기술이 줏대없고 5년이상 못가는 기술은 기술이라 생각하지 않기때문에 AI스킬이 기술이라고 생각도 안함.
+
+아무튼 AI나온뒤로 말로만 쉽게 내뱉는 사람들이 점점 많아져서 피곤함..
+뭔가 멍청한 사람들에게 AI라는 무기를 잘못 쥐어준 느낌이다.
+기존에는 멍청한사람 상대할때 사실과 논리로 쥐어 팼으면 됐는데 이제는 잘못된 지식과 어줍지않은 논리로 무장한 AI사용자랑 상대해야해서 피곤하다.
+
+결론: AI는 재밌는 장난감이다. 진지하게 받아들이지 마라. 물론 시장과 기술은 전혀 상관관계 없으니 주식은 맘놓고 계속해서 AI관련주 사면 된다. 가슴은 차갑게 머리는 뜨겁게.
\ No newline at end of file
diff --git a/content/writing/VTZero.md b/content/writing/VTZero.md
@@ -0,0 +1,37 @@
+---
+title: "VTZero"
+date: 2026-06-27
+tags: []
+---
+
+# 개요
+Virustotal의 vt-py를 활용하여 유의미한 데이터(국가, 소유자, 유해정도) 추철 및 대량 검색기능 제공
+
+https://github.com/devastator-x/VTZero
+
+# 필요조건
+VirusTotal API 키 필요
+API키는 회원가입 하면 자동으로 발급이 되며 키는 `https://www.virustotal.com/gui/user/<Username>/apikey`에서 확인할 수 있다.
+
+# 설정
+1. Repo clone
+```bash
+git clone https://github.com/devastator-x/VTZero.git
+cd your-repo
+```
+
+2. 의존성 설치
+```bash
+pip install -r requirements.txt
+```
+
+3. 최초 실행시 API값 입력
+```bash
+python VTZero.py
+Please enter your VirusTotal API key:
+```
+
+# 사용법
+1. 웹 브라우저에서 http://127.0.0.1:5000 접속
+2. IP 주소를 입력하고(한줄에 하나씩) 제출하면 각 IP에 대한 정보를 얻을 수 있음
+3. 유해 값이 1 이상이면 해당 값은 빨간색으로 표시
\ No newline at end of file
diff --git a/content/writing/We_need_to_talk.md b/content/writing/We_need_to_talk.md
@@ -0,0 +1,67 @@
+---
+title: "We need to talk"
+date: 2026-08-20
+tags: []
+---
+
+
+# EN
+At some point, I seem to have forgotten why I originally wanted to become an engineer.
+
+Why did I want to be an engineer?
+I wanted to create and build something of my own.
+I wanted to build my own fortress so as not to be shaken by the world around me.
+That is why I became an engineer. I believe the motivation for most engineers and developers is probably similar.
+The intoxicating, drug-like sense of accomplishment that comes from creating something—once you taste that achievement, it is hard to turn back.
+
+However, it seems an era has arrived where the engineering profession is no longer a solid fortress, but a sandcastle that could collapse at any moment.
+Engineers are no longer safe, either.
+We could have taken the easy road, so why did we choose the uncomfortable path of an engineer?
+Didn't we choose it because the immense satisfaction it brings outweighs the discomfort?
+Yet, from a certain point onward, it seems engineers have also started looking for the easy way out.
+And now, there appear to be more engineers taking the comfortable path than those walking the difficult one.
+
+We know that the direction and life cycle of all things is not a smooth upward curve, but a staircase graph that endlessly fluctuates up and down.
+Yet, despite knowing this, we all dream of that smooth, continuous rise. At some point, even the smartest people started chasing this illusory graph.
+
+But I do not believe in it.
+A perfectly smooth upward curve cannot exist, nor should it.
+If it does exist, it is merely a trick of the gods; it is a trajectory that has no place in human affairs.
+When everyone else is dreaming empty dreams, the ones who face the uncomfortable reality and hold their ground are the ones whose true value shines the most—especially in times like these.
+I believe society needs these kinds of people now more than ever.
+
+Therefore, I have decided to follow the systems humanity has built over thousands of years.
+Because I firmly believe that the humanity which has survived this long is neither one-dimensional nor solely outcome-driven, I feel no need to forcefully conform or change myself.
+Rather, I believe that one day, everyone will wake from this dream and remember the greatness of humanity once again.
+I hold onto the hope that humanity is not so shallow as to watch monkeys or parrots mimicking humans and desperately wish to become like them.
+
+# KR
+언젠가 부터 내가 엔지니어가 되고자 했던 이유를 망각하고 살아왔던거 같다.
+내가 왜 엔지니어가 되고 싶었을까
+나만의 것을 창조하고 만들고 싶었다.
+주변으로부터 휘둘리지 않기 위해 나만의 성을 구축하고 싶었다.
+그래서 엔지니어가 됐다.
+대부분의 엔지니어, 개발자들의 동기는 비슷할 것이라고 본다.
+뭔가를 만들어내는데에 있어서의 마약같은 성취감.
+그 성취감을 맛보면 돌아가기 힘들다.
+
+근데 이 엔지니어라는 직업도 점점 단단한 성이 아닌 모래성을 쌓아 언제든지 무너질수 있는 시대가 온거 같다.
+엔지니어도 이제 안전하지가 않다.
+우리는 왜 편한길을 놔두고 엔지니어라는 불편한길을 선택했을까?
+불편한길을 선택했음에도 만족감을 주는게 커서 선택하지 않았을까?
+근데 어느순간부터 엔지니어도 점점 편한길을 찾기 시작한거 같다.
+그리고 지금은 편한길을 선택한 엔지니어가 불편한길을 걷는 엔지니어보다 많아진것 같다.
+
+우리는 모든 방향과 생명주기가 부드러운 우상향 그래프가 아닌 수없이 위아래로 요동치는 계단식 그래프인것을 알고 있다.
+하지만 알고 있음에도 불구하고 우리는 모두 부드러운 우상향 그래프를 꿈꾸고 있다.
+언젠가 부터 이 허황된 그래프에 똑똑한 사람들도 탑승하고 있다.
+
+하지만 나는 믿지 않는다.
+매끄러운 우상향 그래프란 있을 수 없고 존재해서도 안되는 그래프이다.
+만약 있다면 그건 신의 장난이지 인간사에 있어서 존재해서는 안되는 그래프이다.
+모두가 허황된 꿈을 꾸고 있을때 불편한 현실을 직시하고 자신만의 중심을 잡는 사람이야 말로 이럴때 일수록 더욱더 희소 가치가 오른다고 생각하고 이런 사람들이 사회에 더욱더 필요하다 생각한다.
+
+그래서 나는 수천년 동안 인류가 만들어온 시스템을 따라가기로 했다.
+그동안 살아남은 인류가 단편적이고 결과론적인 인류가 아님을 굳게 믿기때문에 억지로 맞추고 바꿀 필요가 없다 생각한다.
+아니 언젠가는 모두가 꿈에서 깨고 다시 인류의 위대함을 잊지 않을것이라 믿고 있다.
+인간을 흉내내는 원숭이나 앵무새를 보고 그들이 되고 싶어 안달날 정도로 인류가 허접하지 않다는 희망이 있다.
\ No newline at end of file
diff --git a/content/writing/arp-spoofing.md b/content/writing/arp-spoofing.md
@@ -0,0 +1,77 @@
+---
+title: "ARP Spoofing"
+date: 2024-11-05
+tags: []
+---
+
+**ARP**(주소 결정 프로토콜, Address Resolution Protocol)의 취약점을 악용하여 네트워크 통신을 가로채거나, 변조하는 행위. 공격자는 잘못된 ARP 응답을 통해 자신이 신뢰할 수 있는 다른 호스트인 것처럼 속이고, 이를 통해 트래픽을 훔치거나 수정할 수 있다.
+
+## How ARP spoofing works
+
+ARP Spoofing 은 공격자가 네트워크 상에서 잘못된 ARP 응답을 전송하여, 다른 장치들이 자신의 MAC 주소를 신뢰할 수 있는 장치(예: 게이트웨이, 서버 등)의 MAC 주소로 잘못 인식하게 만든다. 이로 인해, 공격자는 네트워크 트래픽을 가로채거나 변조할 수 있다.
+
+### 공격자가 잘못된 ARP 응답을 보냄
+
+공격자는 피해자 장치와 게이트웨이(또는 네트워크 상의 다른 중요한 장치)에게 잘못된 ARP 응답을 전송하여, 자신의 MAC 주소를 상대방이 원하는 장치의 MAC 주소로 속인다.
+Example. 공격자는 게이트웨이에게 자신의 MAC 주소를 피해자의 IP 주소와 연결된 것으로 알려주고, 동시에 피해자에게 자신의 MAC 주소를 게이트웨이의 IP 주소와 연결된 것으로 알려준다.
+
+
+
+*타겟 호스트(172.16.36.12)와 L2 SW Gateway(172.16.36.1)을 상대로 arp spoofing*
+
+
+
+*공격 전 target host의 MAC Address*
+
+
+
+*공격 후 target host의 MAC Address (Attacker host의 MAC Address 로 변경된 것 확인)*
+
+
+
+*Attacker host의 MAC Address*
+
+
+
+*ARP Apoofing 공격 패킷*
+
+
+
+*ARP Apoofing 공격 패킷*
+
+
+
+
+
+*공격 성공 후 target host에서 nas(91.212.102.100:5000) 와 naver.com 에 접근하려는 요청 트래픽 Attacker host에서 확인 완료*
+
+트래픽이 Attacker host로 들어오지만 따로 포트포워딩을 설정하지 않아 Attacker host에서 트래픽이 멈춰버린다.
+
+정확한 MITM 공격을 구현하려면 트래픽이 멈추는것을 방지하기 위해 포트포워딩 설정을 하여 트래픽이 실제 게이트웨이로 다시 전달해줘야 한다.
+
+
+
+
+
+*공격 종료 후 ARP Cache table에서 target host의 MAC Address가 정상적으로 돌아온것을 확인 할 수 있다.*
+
+### 피해자와 게이트웨이의 ARP 캐시 변경
+
+공격자의 잘못된 ARP 응답을 받은 피해자와 게이트웨이는 공격자의 MAC 주소를 게이트웨이 또는 피해자의 IP 주소에 연결된 것으로 인식함. 이로 인해, 피해자의 트래픽이 공격자를 통해 전달.
+
+### 트래픽 가로채기 또는 변조
+
+공격자는 피해자의 모든 네트워크 트래픽을 가로챌 수 있다. 가로챈 트래픽을 단순히 모니터링할 수 있고, 변조하여 원하는 대로 조작할 수도 있다. 이를 **Main in the middle 공격**(중간자 공격) 이라고 부르며, 공격자는 중간에서 트래픽을 가로채서 읽고, 변조한 뒤, 다시 게이트웨이로 보내 정상적으로 통신이 이루어지는 것처럼 속일 수 있다.
+
+## Effects of ARP spoofing
+
+1. Man-in-the-Middle 공격 (MITM) : 공격자는 중간에서 트래픽을 가로채어 내용을 읽고, 변조할 수 있다. 이를 통해 중요한 정보(비밀번호, 쿠키, 세션 토큰 등)를 탈취할 수 있다.
+2. 서비스 거부 공격(DoS) : 공격자는 ARP Spoofing을 통해 네트워크 상의 특정 장치에 대한 트래픽을 차단하거나, 특정 장치가 게이트웨이와의 통신을 할 수 없게 만들어 네트워크에 장애를 일으킬 수 있다.
+3. 트래픽 재전송 : 공격자는 트래픽을 가로챈 뒤, 원래 목적지로 트래픽을 다시 보내 피해자가 공격 사실을 알아차리지 못하게 할 수 있다.
+
+## How to defend against ARP spoofing
+
+1. 동적 ARP 검사(Dynamic ARP Inspection, DAI) : 네트워크 스위치에서 ARP 트래픽을 모니터링하여, 의심스러운 ARP 패킷을 필터링한다. 이를 통해, 잘못된 ARP 요청이나 응답이 네트워크에 전달되지 않도록 할 수 있다.
+2. ARP 캐시 고정(Static ARP Entries) : 중요한 장치(Ex. Gateway)에 대해 고정된 ARP 엔트리를 설정하여, 잘못된 ARP 정보가 캐시에 추가되지 않도록 할 수 있다. 다만, 큰 네트워크에서는 관리가 복잡할 수 있다.
+3. 암호화 사용(HTTPS, SSH) : ARP 스푸핑 공격을 통해 중간에서 데이터를 가로채더라도, 암호화된 통신을 사용하면 데이터를 탈취당하더라도 해독할 수 없다.
+4. VPN 사용 : 가상 사설망(VPN)을 사용하면, 클라이언트와 서버 간의 모든 트래픽이 암호화되기 때문에 ARP 스푸핑 공격을 통해 데이터를 탈취하더라도 내용을 확인 할 수 없음.
diff --git a/content/writing/arp.md b/content/writing/arp.md
@@ -0,0 +1,19 @@
+---
+title: "ARP"
+date: 2024-11-05
+tags: []
+---
+
+**ARP**(주소 결정 프로토콜, Address Resolution Protocol)는 IP 주소를 MAC 주소로 변환해 주는 프로토콜. 네트워크 상에서 두 장치가 통신할 때, 장치들은 서로의 IP 주소를 알고 있지만, 실제 데이터 전송을 위해서는 상대 장치의 MAC 주소(Physical Address) 가 필요하다. ARP는 이를 위해 사용.
+
+## ARP의 기본적인 동작 방식
+
+**ARP 요청(ARP Request)**: 네트워크 장치(A)가 네트워크 상에서 다른 장치(B)의 주소를 알고 있을 때, 장치 A는 “이 IP 주소의 MAC 주소가 무엇인가?” 라고 묻는 ARP 요청을 네트워크 브로드캐스트로 보낸다.
+
+**ARP 응답(ARP Reply)**: 해당 IP 주소를 가진 장치(B)가 “내 MAC 주소는 이거야” 라고 응답하고, 장치 A는 이 정보를 ARP 캐시에 저장한다. 이후로 장치 A는 IP 주소를 MAC 주소로 변환하는 작업을 생략하고 데이터를 바로 보낼 수 있다.
+
+
+
+## ARP의 취약점
+
+ARP 프로토콜은 신뢰할 수 있는 인증 절차가 없기 때문에 매우 취약하다. ARP 요청이나 응답에는 신뢰할 수 있는 출처 검증 과정이 없기 때문에, 누구든지 네트워크 상에서 잘못된 정보를 주고받을 수 있다. 이는 공격자가 쉽게 악용할 수 있는 요소이다.
diff --git a/content/writing/bind9-dns-install.md b/content/writing/bind9-dns-install.md
@@ -0,0 +1,152 @@
+---
+title: "Bind 9 DNS Server 설치"
+date: 2024-11-05
+tags: []
+---
+
+AWS Lightsail Amazon linux 인스턴스 호스트에 BIND 9 DNS Server 설치 후 설정까지 진행 해봤다.
+
+## BIND 9 설치 및 기본 설정
+
+**패키지 매니저 업데이트**
+
+```bash
+sudo yum update -y
+```
+
+**BIND 9 설치**
+
+```bash
+sudo yum install -y bind bind-utils
+```
+
+**BIND 9 설정**
+
+```bash
+sudo vi /etc/named.conf
+```
+
+**모든 IP에서 쿼리를 허용**
+
+```
+allow-query { any; };
+```
+
+**IPv6 주소에서 요청을 수신하지 않도록 설정**
+
+```
+options {
+ listen-on-v6 { none; };
+};
+```
+
+## 보안 옵션 설정
+
+### DNSSEC (DNS Security Extensions) 사용
+
+DNSSEC은 DNS 쿼리 응답이 인증되지 않은 경우, 그 응답을 무효화함으로써 스푸핑 공격을 막는 데 효과적. DNS 서버에서 DNSSEC을 사용하면 클라이언트는 응답이 신뢰할 수 있는지 확인할 수 있음.
+
+**DNSSEC 활성화(Default)**
+
+```
+options {
+ dnssec-enable yes;
+ dnssec-validation yes;
+};
+```
+
+### DNS 캐시 중독 (DNS Cache Poisoning) 방지
+
+캐시 중독은 공격자가 잘못된 데이터를 DNS 서버의 캐시에 저장하여, 그 데이터를 클라이언트에게 반환하도록 만드는 공격이다. 재귀적인 DNS 서버에서 문제가 될 수 있다.
+
+**무작위 포트 및 ID 사용(DNSSEC만 활성화 해도 충분)**
+
+쿼리의 소스 포트와 트랜잭션 ID를 무작위로 사용하여 예측 불가능하게 만듦으로써 캐시 중독 공격을 어렵게한다.
+
+```
+options {
+ query-source address * port *;
+};
+```
+
+### DDoS 공격 (DNS Amplification) 방지
+
+DNS 증폭 공격은 DNS 서버를 이용해 공격 대상에게 대량의 트래픽을 전송하는 DDoS 공격의 일종이다.
+
+**Rate Limiting (RRL) 적용(BIND 9.9.4 이상에서 기본적으로 지원)**
+
+rate-limit 옵션 추가
+
+```
+options {
+ rate-limit {
+ responses-per-second 10; # 초당 응답 제한 (기본값은 5)
+ window 5; # RRL 윈도우 크기 (초 단위)
+ slip 2; # 1/x 확률로 응답을 전송함 (2 = 50%)
+ qps-scale 250; # 응답 제한을 조정할 클라이언트 수 (기본값은 1000)
+ ipv4-prefix-length 24; # IPv4 네트워크 그룹화 범위 (/24 기본값)
+ ipv6-prefix-length 56; # IPv6 네트워크 그룹화 범위 (/56 기본값)
+ exempt-clients { localhost; }; # RRL 제한에서 제외할 클라이언트
+ };
+};
+```
+
+- responses-per-second : 한 클라이언트 IP에 대해 초당 허용되는 응답 수. 초과하는 경우 응답을 제한한다. 기본값은 5이며, 이를 10으로 설정한다.
+- window : 쿼리를 계산할 시간 창을 설정한다. 5초 동안 응답을 계산하는 시간 창을 설정한다.
+- slip : RRL이 활성화된 경우, 몇 개의 쿼리 응답을 드롭하고 몇 개를 허용할지 설정한다. slip 2는 50% 확률로 응답을 전송하겠다는 뜻이다.
+- qps-scale : 응답 제한을 조정할 클라이언트의 수를 설정한다.
+- ipv4-prefix-length 및 ipv6-prefix-length : 네트워크 그룹화에 사용할 네트워크 길이를 설정한다.
+- exempt-clients : RRL 제한에서 제외할 클라이언트를 설정한다.
+
+### BIND 9 서비스 재시작 및 부팅 시 자동 실행 설정
+
+```bash
+sudo systemctl restart named
+sudo systemctl enable named
+```
+
+### 해당 인스턴스 방화벽 규칙에 DNS(53 / TCP, UDP) 추가
+
+
+
+### BIND 9 서버가 제대로 설정되었는지 확인
+
+
+
+## 클라이언트에서 테스트
+
+Windows
+
+```sh
+nslookup example.com <BIND 9 서버의 IP 주소>
+```
+
+
+
+Linux
+
+```bash
+dig @<BIND 9 서버의 IP 주소> example.com
+```
+
+
+
+
+
+*DNS 요청, 응답 패킷 확인*
+
+### 테스트 결과 정상이라면 DNS 서버 변경
+
+**Linux에서 DNS 서버 설정 변경**
+
+/etc/resolv.conf 파일을 편집하여 BIND 9 서버의 IP 주소를 지정
+
+```bash
+sudo vim /etc/resolv.conf
+```
+
+파일에 다음을 추가
+
+```
+nameserver <BIND 9 서버의 IP 주소>
+```
diff --git a/content/writing/doh.md b/content/writing/doh.md
@@ -0,0 +1,49 @@
+---
+title: "DoH(DNS over HTTPS)"
+date: 2024-11-08
+tags: []
+---
+
+Securelist의 [SteelFox Trojan 분석 보고서](https://securelist.com/steelfox-trojan-drops-stealer-and-miner/114414/?ref=openpesto.com)에서 C2서버와 피해호스트 통신 과정에서 DoH라는 기술을 이용하여 도메인 조회 과정을 숨기는 페이로드를 구현했다는 내용이 있어서 DoH 기술에 대해 알아보았다.
+
+## DoH(DNS over HTTPS)
+
+웹 트래픽을 통해 DNS 요청을 암호화하여 보호하는 프로토콜이다. 이 프로토콜은 전통적인 DNS 요청을 HTTP로 암호화해 전송하므로, DNS 요청을 외부에서 가로채거나 조작하는 공격으로부터 보호하는 장점이 있다.
+
+### DNS over HTTPS의 주요 개념과 동작 방식
+
+1. DNS 요청의 기본 원리 • DNS(Domain Name System)는 웹 주소(예: google.com)를 IP 주소(예: 142.250.74.14)로 변환하여 서버와의 연결을 가능하게 한다. • 기존 DNS 요청은 일반적으로 암호화되지 않은 상태로 전송되기 때문에 가로채기나 조작이 가능한 위험있다.
+2. DoH의 암호화 방식 • DoH는 HTTPS(포트 443) 를 통해 DNS 요청을 전송하여 모든 요청을 암호화한다. • 이를 통해 제 3자가 DNS 요청을 볼 수 없으며, 사용자의 웹 브라우징 기록이나 위치 정보 등을 추적하는 것을 어렵게 만든다.
+3. DoH의 동작 원리 • DoH를 사용하는 브라우저나 애플리케이션은 HTTPS를 통해 도메인 이름을 IP 주소로 변환하는 요청을 전송한다. • 이 요청은 DoH 서버(예: Google DNS, Cloudflare, Mozilla에서 제공하는 서버)에 전달되며, 서버는 응답을 HTTPS로 암호화해 되돌려준다.
+
+### DoH의 장점
+
+1. 보안 강화 • DoH는 암호화된 연결을 통해 DNS 요청을 전송하므로, 스누핑이나 MITM(Man-In-The-Middle) 공격으로부터 보호된다. • ISP나 네트워크 관리자 등이 사용자의 DNS 트래픽을 추적하거나 조작할 수 없다.
+2. 프라이버시 보호 • DNS 요청이 HTTPS로 암호화되기 때문에 사용자의 인터넷 사용 기록을 외부에서 추적하기 어려워진다. • 특히 공용 네트워크에서 사용자의 브라우징 프라이버시를 보호할 수 있다.
+3. 검열 회피 • DoH는 HTTPS 트래픽과 구분되지 않기 때문에, 특정 DNS 요청을 차단하려는 검열 정책을 우회하는데 도움이 될 수 있다.
+
+### DoH의 단점
+
+1. 지연 시간 증가 • HTTPS 암호화 및 복호화 과정이 추가되므로, 전통적인 DNS에 비해 응답 시간이 조금 느려질 수 있다.
+
+### DoH 활성화 방법
+
+- 웹 브라우저에서 활성화
+ - Google Chrome과 Mozilla Firefox 등에서는 DoH를 직접 활성화할 수 있는 옵션을 제공한다.
+ - Mozilla Firefox 기준: 설정 > 개인 정보 및 보안 > 다음을 사용하여 DNS over HTTPS 활성화 > 최대 보호
+- 운영 체제에서 활성화
+ - Windows 11 이상에서는 시스템 DNS 설정에서 DoH를 설정할 수 있으며, macOS나 Linux의 경우 네트워크 설정을 통해 설정이 가능하다.
+ - Windows 11 기준: Settings > Network & internet > Ethernet > Edit DNS settigs > Preferred DNS 1.1.1.1(Cloudflare) 로 설정 > DNS over HTTPS On(automatic template) > Alternate DNS 1.1.1.2(Cloudflare) 로 설정 > DNS over HTTPS On(automatic template)
+
+### DoH 상태 확인 방법
+
+- Cloudflare DoH 테스트: https://1.1.1.1/help
+- Google DoH 테스트: https://dns.google/
+
+
+
+*DoH ON*
+
+
+
+*DoH OFF*
diff --git a/content/writing/dont-assume-you-cant-control-yourself.md b/content/writing/dont-assume-you-cant-control-yourself.md
@@ -0,0 +1,29 @@
+---
+title: "본인을 통제하지 못한다고 착각하지 마라"
+date: 2025-10-16
+tags: []
+---
+
+대 AI, 대 정부 시대에 본인을 통제하기에는 이미 늦어버렸다고 포기하지 마라
+
+마음만 먹으면 충분히 본인을 통제할 수 있다.
+
+카카오톡, SNS 다 끊었다.
+
+못하면 죽는가?
+
+못하면 사회에서 살아갈 수가 없는가?
+
+충분히 살아갈 수 있다.
+
+본인의 삶은 본인이 컨트롤 하는거다.
+
+남에게 맞기지 마라.
+
+인간으로서의 자유의지를 포기하고 남에게 이양하지 마라.
+
+편리함이라는 유혹에서 본인의 자유를 거래하지마라.
+
+인간으로서 자신의 자유란 그 어느것과도 바꿀 수 없는 것이다.
+
+고작 3000원 5000원에 본인의 자유를 팔지 말아라.
diff --git a/content/writing/even-inappropriateness-has-limits.md b/content/writing/even-inappropriateness-has-limits.md
@@ -0,0 +1,55 @@
+---
+title: "부적절한 것도 정도가 있어!"
+date: 2025-10-17
+tags: []
+---
+
+다양성과 소통이 부각되는 시대에
+
+본질은 사라지고 있다
+
+단순히 다양성을 존중하고 단순히 소통을 하는건 안하니만 못하다
+
+오히려 독이된다.
+
+다양성을 존중함으로서 길을 해매고
+
+단순히 쓸때없는 표면적인 소통만 늘어나면서 서로 상처받을까 말을 빙빙돌려서 말하고 본질은 하나도 없는 빈말만 오고간다
+
+그렇게 오고감에따라 소통은 결국 피로감을 유발하고 피로감에 의해 진실된 대화는 하지 못한채 의미없는 말만 왔다갔다 할 뿐이다.
+
+그러고는 본인은 소통을 많이하고 중요시한다고 강조한다.
+
+소통을 자주하고 많이한다고 좋은 조직이였으면 개나소나 다 좋은 조직 됐겠다.
+
+매번 느끼는거지만 요즘 너무 단순하게 세상을 바라보는 편협한 시각을 가진 사람이 많다고 느낀다.
+
+다양성을 존중하고 세상이 하도 복잡하다보니 가만히만 있어도 피로감이 쌓이니 넓은 시야와 생각을 가질 여유조차 사라진다.
+
+그렇게 편협한 시각을 가지게 되고 편협한 시각으로 판단하고 빠르게 효율적으로 결론을 내버린다.
+
+입체적으로 보고 입체적인 정보를 바탕으로 본인 스스로 판단하고 옳고 그름을 고찰하기에는 너무 비효율 적이고 피곤하다.
+
+그렇게 단순히 숫자상으로 시간상으로 양적으로 소통을 많이했다고 자부심을 느낀다.
+
+하지만 그 많은 알맹이들을 열어보면 씨알이 하나도 없다.
+
+이미 지금 세상은 너무 복잡하고 바쁘기 때문에 극한의 효율을 추구하는 시대를 피할 수는 없다.
+
+그렇다고 이 가만히만 있어도 피곤한 사회에서 입체적인 시각을 가지면서 까지 비효율을 추구해야 할까?
+
+아니, 방법이 있다.
+
+양보다 질에 집중하자. 당연하겠지만 10명하고 의미없는 대화 나눌 바에 한명과 깊은 대화를 나누는게 더 양질의 정보를 얻을 가능성이 크다.
+
+물론 여기서 중요한점은 그 한명과 소통을 할때 말을 빙빙 돌리거나 쿠션언어를 쓰느라 머리 싸움 할 필요가 없다.
+
+예전에 팀장님과 면담 할때도 본론으로 넘어가지 않고 본론 주변에 계속 맴돌고 할 말이 있는데 못하시는 것 처럼 보여서 그냥 편하게 말해달라고 했다. 그게 서로간에 에너지도 덜 잡아먹고 의미없는 말로 인해 오해가 생기는걸 방지 할 수 있다고 생각했기 때문이다. 그냥 하고싶은말, 본론 부터 이야기 하는게 더 긍정적인 대화와 결과가 도출 될거라 난 생각한다.
+
+‘의미없는’말을 줄인 대화는 집중하게 되고 집중한 만큼 입체적으로 시야를 볼 수 있는 여유도 생긴 대화는 열어보면 씨알이 알차다.
+
+나는 나이가 들어감에 따라 집중력이 짧아진다는 말을 믿는다.
+
+안타깝지만 짧아진 집중력을 인정하고 짧아진 집중력 시간 안에 좋은 결과를 얻었으면 한다.
+
+여자친구와 드라마 “부적절한 것도 정도가 있어!” 를 보면서 나눴던 대화이다.
diff --git a/content/writing/how-to-using-sysmon-for-Linux.md b/content/writing/how-to-using-sysmon-for-Linux.md
@@ -0,0 +1,356 @@
+---
+title: "How to using sysmon for Linux"
+date: 2025-12-07
+tags: []
+---
+
+# Summary
+
+Sysmon for Linux는 마이크로소프트 Sysinternals 도구 중 하나인 시스템 모니터(System monitor) 를 리눅스 환경으로 포팅한 강력한 보안 모니터링 도구이다.
+
+윈도우 환경에서 침해 사고 분석과 위협 탐지의 표준처럼 쓰이던 Sysmon을 리눅스에서도 사용할 수 있다.
+
+## A. 핵심 원리: eBPF (Extended Berkeley Packet Filter)
+
+Sysmon for Linux가 기존의 리눅스 감사 도구(auditd 등) 와 가장 차별화되는 점은 **eBPF**를 사용한다는 것이다.
+
+- 동작 방식 : 커널 소스 코드를 수정하거나 모듈을 로드하지 않고도, 커널 내부의 특정 이벤트(시스템 호출, 네트워크 패킷 등) 를 안전하고 효율적으로 후킹(Hooking) 하여 모니터링한다.
+- 장점 : 시스템 안정성을 해치지 않으면서 고성능으로 데이터를 수집할 수 있다. (과거 커널 모듈 방식의 시스템 충돌 위험을 최소화함)
+
+## B. 주요 기능 및 모니터링 항목
+
+Sysmon은 윈도우 버전과 거의 동일한 이벤트 ID 체계를 사용하므로, 윈도우와 리눅스가 섞인 혼합 환경에서 통합 관제(SIEM) 를 구축하기 매우 유리하다.
+
+주요 이벤트 ID는 다음과 같다.
+
+- Event ID 1 (Process Creation) : 어떤 프로세스가 실행되었는지, 부모 프로세스는 무엇인지, 실행 시 명령어 인자(Command Line)는 무엇인지 기록한다. (가장 중요)
+- Event ID 3 (Network Connection) : 프로세스가 외부와 통신할 때 IP, 포트, 프로토콜 정보를 기록한다.
+- Event ID 5 (Process Terminate) : 프로세스가 언제 종료되었는지 기록한다.
+- Event ID 9 (RawAccessRead) : 드라이브 섹터에 대한 직접 읽기를 탐지한다.
+- Event ID 11 (FileCreate) : 파일이 생성되거나 덮어써질 때를 기록한다. (랜섬웨어 탐지 등에 유용)
+
+## C. 로그 저장 위치
+
+윈도우에서는 '이벤트 뷰어'에 저장되지만, 리눅스에서는 표준 로깅 시스템을 따른다.
+
+- Syslog : /var/log/syslog (또는 /var/log/messages)
+- Journald : journalctl 명령어로 조회 가능
+- 로그 포맷은 XML 형태를 텍스트로 변환하여 저장하므로, Splunk나 ELK 같은 SIEM 도구로 전송하여 파싱하기좋다.
+
+## D. 기존 리눅스 도구(Auditd)와의 비교
+
+| 특성 | Sysmon for Linux | Auditd (Linux Audit Framework) |
+| --- | --- | --- |
+| 기반 기술 | eBPF (최신, 고성능) | Kernel Audit Subsystem (전통적) |
+| 로그 가독성 | 높음 (구조화된 데이터, 관계 파악 용이) | 낮음 (해석이 어렵고 파싱이 까다로움) |
+| 필터링 | 강력함 (XML 기반의 상세 조건 설정) | 가능하지만 규칙 작성이 복잡함 |
+| 플랫폼 통합 | Windows/Linux 로그 포맷 통일 가능 | 리눅스 전용 |
+| 상태 | 실행 프로세스 트리 추적 용이 | 단일 이벤트 위주 |
+
+## E. 설치
+
+### E.1. 마이크로소프트 저장소 키 및 패키지 등록
+
+```bash
+# 1. 설치 파일 다운로드
+$ wget -q https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
+
+# 2. 다운로드한 패키지 설치 (저장소 등록)
+$ sudo dpkg -i packages-microsoft-prod.deb
+```
+
+### E.2. Sysmon 설치
+
+```bash
+# 1. 패키지 목록 업데이트
+$ sudo apt-get update
+
+# 2. Sysmon for Linux 설치
+$ sudo apt-get install sysmonforlinux
+```
+
+## F. 설정 파일 생성
+
+설정파일(XML) 을 지정하고 서비스를 시작해야 Sysmon 이 동작 한다.
+
+#### sysmon-config.xml
+
+```xml
+<Sysmon schemaversion="4.81">
+ <HashAlgorithms>sha256</HashAlgorithms>
+
+ <EventFiltering>
+
+ <RuleGroup name="Terminate_Silence" groupRelation="or">
+ <ProcessTerminate onmatch="include">
+ <Image condition="is">THIS_PROCESS_WILL_NEVER_EXIST_12345</Image>
+ </ProcessTerminate>
+ </RuleGroup>
+
+ <RuleGroup name="ProcessCreate_Security" groupRelation="or">
+
+ <ProcessCreate onmatch="exclude">
+ <Image condition="end with">gnome-shell</Image> <Image condition="end with">tracker-miner-fs-3</Image> <Image condition="end with">vmtoolsd</Image> <Image condition="is"><unknown process></Image> <CommandLine condition="contains">tmux-continuum</CommandLine> <CommandLine condition="contains">continuum_save.sh</CommandLine> <CommandLine condition="contains">check_tmux_version.sh</CommandLine> <CommandLine condition="contains">cpuUsage.sh</CommandLine> <CommandLine condition="contains">extensionHost</CommandLine> <CommandLine condition="contains">which ps</CommandLine> <CommandLine condition="contains">/usr/bin/ps</CommandLine> </ProcessCreate>
+
+ <ProcessCreate onmatch="include">
+ <Image condition="end with">bash</Image>
+ <Image condition="end with">sh</Image>
+ <Image condition="end with">dash</Image>
+ <Image condition="end with">zsh</Image>
+
+ <Image condition="end with">sudo</Image>
+ <Image condition="end with">su</Image>
+
+ <Image condition="end with">python3</Image>
+ <Image condition="end with">perl</Image>
+ <Image condition="end with">gcc</Image>
+ <Image condition="end with">g++</Image>
+
+ <Image condition="end with">curl</Image>
+ <Image condition="end with">wget</Image>
+ <Image condition="end with">nc</Image> <Image condition="end with">ncat</Image>
+ <Image condition="end with">ssh</Image> <Image condition="end with">scp</Image> <Image condition="end with">ftp</Image>
+ <Image condition="end with">socat</Image>
+ </ProcessCreate>
+ </RuleGroup>
+
+ <RuleGroup name="Network_Security" groupRelation="or">
+ <NetworkConnect onmatch="include">
+ <Image condition="end with">curl</Image>
+ <Image condition="end with">wget</Image>
+ <Image condition="end with">nc</Image>
+
+ <Image condition="end with">bash</Image>
+ <Image condition="end with">sh</Image>
+
+ <Image condition="end with">python3</Image>
+
+ <Image condition="end with">ssh</Image>
+ </NetworkConnect>
+ </RuleGroup>
+
+ </EventFiltering>
+</Sysmon>
+```
+
+## G. 설정 적용
+
+### G.1 만든 파일을 로드하여 Sysmon을 시작 or 재시작
+
+```bash
+$ sudo sysmon -i sysmon-config.xml
+```
+
+### G.2 이미 실행 중이라면 설정을 업데이트(-c)
+
+```bash
+$ sudo sysmon -c sysmon-config.xml
+```
+
+## H. 로그 확인하기
+
+```bash
+# Ubuntu의 기본 시스템 로그 파일 모니터링
+$ sudo tail -f /var/log/syslog | grep "Linux-Sysmon"
+```
+
+### H.1 가독성이 떨어지는 로그
+
+```bash
+2025-12-10T22:27:58.152234+09:00 x sysmon: <Event><System><Provider Name="Linux-Sysmon" Guid="{ff032593-a8d3-4f13-b0d6-01fc615a0f97}"/><EventID>1</EventID><Version>5</Version><Level>4</Level><Task>1</Task><Opcode>0</Opcode><Keywords>0x8000000000000000</Keywords><TimeCreated SystemTime="2025-12-10T13:27:58.151790000Z"/><EventRecordID>16525</EventRecordID><Correlation/><Execution ProcessID="1296720" ThreadID="1296720"/><Channel>Linux-Sysmon/Operational</Channel><Computer>x</Computer><Security UserId="0"/></System><EventData><Data Name="RuleName">ProcessCreate_Security</Data><Data Name="UtcTime">2025-12-02 06:14:11.408</Data><Data Name="ProcessGuid">{3e748dcd-83b3-692e-d10b-c636f7560000}</Data><Data Name="ProcessId">1979575</Data><Data Name="Image">/usr/bin/sudo</Data><Data Name="FileVersion">-</Data><Data Name="Description">-</Data><Data Name="Product">-</Data><Data Name="Company">-</Data><Data Name="OriginalFileName">-</Data><Data Name="CommandLine">sudo tail -f /var/log/syslog</Data><Data Name="CurrentDirectory">/home/devastator/.config</Data><Data Name="User">devastator</Data><Data Name="LogonGuid">{3e748dcd-65f1-6934-e803-000002000000}</Data><Data Name="LogonId">1000</Data><Data Name="TerminalSessionId">3</Data><Data Name="IntegrityLevel">no level</Data><Data Name="Hashes">SHA256=654d953098b5dd26fcc3af44d4cf6e986f61f84ad9198d57722702c6034bd686</Data><Data Name="ParentProcessGuid">{3e748dcd-7503-692e-9553-b55339630000}</Data><Data Name="ParentProcessId">1907076</Data><Data Name="ParentImage">/usr/bin/zsh</Data><Data Name="ParentCommandLine">zsh</Data><Data Name="ParentUser">devastator</Data></EventData></Event>
+2025-12-10T22:28:07.111277+09:00 x sysmon: <Event><System><Provider Name="Linux-Sysmon" Guid="{ff032593-a8d3-4f13-b0d6-01fc615a0f97}"/><EventID>1</EventID><Version>5</Version><Level>4</Level><Task>1</Task><Opcode>0</Opcode><Keywords>0x8000000000000000</Keywords><TimeCreated SystemTime="2025-12-10T13:28:07.110532000Z"/><EventRecordID>16526</EventRecordID><Correlation/><Execution ProcessID="1296720" ThreadID="1296720"/><Channel>Linux-Sysmon/Operational</Channel><Computer>x</Computer><Security UserId="0"/></System><EventData><Data Name="RuleName">ProcessCreate_Security</Data><Data Name="UtcTime">2025-12-02 06:14:20.366</Data><Data Name="ProcessGuid">{3e748dcd-83bc-692e-d15b-68e08b590000}</Data><Data Name="ProcessId">1979719</Data><Data Name="Image">/usr/bin/sudo</Data><Data Name="FileVersion">-</Data><Data Name="Description">-</Data><Data Name="Product">-</Data><Data Name="Company">-</Data><Data Name="OriginalFileName">-</Data><Data Name="CommandLine">sudo</Data><Data Name="CurrentDirectory">/home/devastator/.config</Data><Data Name="User">devastator</Data><Data Name="LogonGuid">{3e748dcd-62c8-6934-e803-000001000000}</Data><Data Name="LogonId">1000</Data><Data Name="TerminalSessionId">3</Data><Data Name="IntegrityLevel">no level</Data><Data Name="Hashes">SHA256=654d953098b5dd26fcc3af44d4cf6e986f61f84ad9198d57722702c6034bd686</Data><Data Name="ParentProcessGuid">{00000000-0000-0000-0000-000000000000}</Data><Data Name="ParentProcessId">1237687</Data><Data Name="ParentImage">-</Data><Data Name="ParentCommandLine">-</Data><Data Name="ParentUser">-</Data></EventData></Event>
+```
+
+하지만 XML 형식의 로그가 쏟아져 나와 가독성이 매우 떨어지므로 무분별한 로그를 정제하기 위해 Python 뷰어 스크립트가 필요하다
+
+## I. 뷰어 스크립트 생성
+
+### sysmon_view.py
+
+```python
+import sys
+import xml.etree.ElementTree as ET
+
+# ==============================================================================
+# [스크립트 설명]
+# Sysmon for Linux가 생성하는 XML 형식의 로그를 실시간으로 파싱
+# 'tail -f /var/log/syslog' 명령어와 파이프(|)로 연결되어 실행
+#
+# [주요 기능]
+# 1. 복잡한 XML 태그 제거 및 데이터 추출
+# 2. 불필요한 노이즈(Event ID 5 등) 필터링
+# 3. 중요 이벤트(프로세스 생성, 네트워크 연결) 컬러 강조
+# 4. 파일 해시(SHA256) 값 출력 지원
+# ==============================================================================
+
+def get_tag_name(elem):
+ """
+ XML 태그에서 네임스페이스(Namespace) URL을 제거하고, 순수 태그 이름만 추출하는 함수
+
+ [예시]
+ 입력: {http://schemas.microsoft.com/schemas/eventlog/main}EventID
+ 출력: EventID
+
+ 이유: Sysmon 로그는 표준 포맷을 따르기 때문에 태그 앞에 긴 URL이 붙어있어,
+ 이를 제거해야 데이터 처리가 쉬움
+ """
+ if '}' in elem.tag:
+ # '}' 문자를 기준으로 쪼개서 뒷부분(진짜 이름)만 가져옴
+ return elem.tag.split('}', 1)[1]
+ return elem.tag
+
+def parse_and_print():
+ """
+ 표준 입력(stdin)으로 들어오는 로그를 한 줄씩 읽어서 분석하고 출력하는 메인 함수
+ """
+
+ # 1. 시작 배너 출력 (스크립트가 실행되었음을 알림)
+ print("\n" + "="*70)
+ print(" 🛡️ Sysmon Log Viewer (Detailed Comment Version)")
+ print(" waiting for security events... (Noise filtered)")
+ print("="*70 + "\n")
+
+ # 2. 실시간 로그 읽기 루프 (파이프로 연결된 데이터가 들어올 때마다 반복)
+ for line in sys.stdin:
+ try:
+ # Sysmon 로그인지 확인 (로그 라인에 <Event> 태그가 있는지 검색)
+ start_idx = line.find('<Event>')
+
+ # <Event> 태그가 없으면 Sysmon 로그가 아니거나 빈 줄이므로 건너뜀
+ if start_idx == -1:
+ continue
+
+ # XML 데이터 부분만 잘라냄
+ xml_data = line[start_idx:]
+
+ # 3. XML 파싱 (문자열을 구조화된 데이터로 변환)
+ try:
+ root = ET.fromstring(xml_data)
+ except ET.ParseError:
+ # XML 형식이 깨져있으면 무시하고 다음 줄로 넘어감
+ continue
+
+ # 데이터를 저장할 변수 초기화
+ event_id = "Unknown"
+ data_map = {} # <Data Name="Key">Value</Data> 형태를 저장할 딕셔너리
+
+ # 4. XML 내부 순회 및 데이터 추출
+ for elem in root.iter():
+ tag = get_tag_name(elem) # 태그 이름 정제
+
+ # 이벤트 ID 추출 (<EventID>1</EventID>)
+ if tag == 'EventID':
+ event_id = elem.text
+
+ # 상세 데이터 추출 (<Data Name="Image">/usr/bin/sudo</Data>)
+ elif tag == 'Data':
+ name_attr = elem.attrib.get('Name') # Name 속성 값 가져오기 (예: Image, User)
+ if name_attr:
+ # 텍스트가 있으면 가져오고, 없으면 빈 문자열 저장
+ data_map[name_attr] = elem.text if elem.text else ""
+
+ # 5. 필터링
+ # Event ID 5 (Process Terminate)는 노이즈가 되므로 출력하지 않음
+ if event_id == "5":
+ continue
+
+ # 6. 출력에 사용할 공통 필드 가져오기 (없으면 기본값 사용)
+ time = data_map.get('UtcTime', 'Unknown Time') # 발생 시간
+ image = data_map.get('Image', 'Unknown Process') # 실행된 프로세스 경로
+ user = data_map.get('User', '-') # 실행한 사용자
+
+ # 7. ANSI 색상 코드 정의 (터미널에서 색을 입히기 위함)
+ RESET = "\033[0m" # 색상 초기화
+ GREEN = "\033[92m" # 초록색 (프로세스 생성)
+ RED = "\033[91m" # 빨간색 (네트워크 연결 - 위험)
+ BLUE = "\033[94m" # 파란색 (파일 생성)
+ GRAY = "\033[90m" # 회색 (부가 정보/해시)
+ BOLD = "\033[1m" # 굵게 강조
+
+ # 8. 이벤트 ID별 출력 포맷 설정
+
+ # [CASE 1] Event ID 1: 프로세스 생성
+ if event_id == "1":
+ cmd = data_map.get('CommandLine', '') # 실행된 명령어 전체
+ hashes = data_map.get('Hashes', '') # SHA256 등 해시값
+
+ # 메인 정보 출력
+ print(f"{GREEN}[CREATE]{RESET} {time} | {BOLD}{image}{RESET} | User: {user} | {cmd}")
+
+ # 해시값이 존재하면, 바로 아랫줄에 회색으로 들여쓰기하여 출력
+ if hashes:
+ print(f" └─ {GRAY}Hash: {hashes}{RESET}")
+
+ # [CASE 2] Event ID 3: 네트워크 연결 (외부 통신)
+ elif event_id == "3":
+ dest_ip = data_map.get('DestinationIp', '-') # 목적지 IP
+ dest_port = data_map.get('DestinationPort', '-') # 목적지 포트
+ protocol = data_map.get('Protocol', 'tcp') # 프로토콜 (tcp/udp)
+
+ print(f"{RED}[NET]{RESET} {time} | {image} -> {dest_ip}:{dest_port} ({protocol})")
+
+ # [CASE 3] Event ID 11: 파일 생성 (악성코드 드롭 등)
+ elif event_id == "11":
+ target = data_map.get('TargetFilename', '-') # 생성된 파일 경로
+
+ print(f"{BLUE}[FILE]{RESET} {time} | {image} created -> {target}")
+
+ # [CASE 4] 그 외 이벤트 (설정 변경 등)
+ else:
+ print(f"[ID {event_id}] {time} | {image}")
+
+ except Exception as e:
+ # 실행 중 예상치 못한 에러가 발생해도 스크립트가 꺼지지 않게 처리
+ # (디버깅이 필요할 땐 아래 pass를 print(e)로 바꾸면 됨)
+ pass
+
+# 스크립트가 직접 실행될 때만 parse_and_print() 함수 실행
+if __name__ == "__main__":
+ parse_and_print()
+```
+
+## J. 뷰어 스크립트 실행
+
+```bash
+$ sudo tail -f /var/log/syslog | python3 -u sysmon_view.py
+```
+
+#### 결과값 예시
+
+```bash
+======================================================================
+ 🛡️ Sysmon Log Viewer (Detailed Comment Version)
+ waiting for security events... (Noise filtered)
+======================================================================
+
+[CREATE] 2025-12-02 05:03:30.832 | /usr/bin/sudo | User: devastator | sudo tail -f /var/log/syslog
+ └─ Hash: SHA256=654d953098b5dd26fcc3af44d4cf6e986f61f84ad9198d57722702c6034bd686
+[CREATE] 2025-12-02 05:03:39.391 | /usr/bin/ssh | User: devastator | ssh
+ └─ Hash: SHA256=ffc312252dd2756e3346f1fd5a680486a4073abef452d44939351742d9b4bba0
+[CREATE] 2025-12-02 05:03:42.230 | /usr/bin/curl | User: devastator | curl google.com
+ └─ Hash: SHA256=aca992dba6da014cd5baaa739624e68362c8930337f3a547114afdbd708d06a4
+[NET] 2025-12-02 05:03:42.271 | /usr/bin/curl -> 127.0.0.1:36697 (udp)
+[NET] 2025-12-02 05:03:42.306 | /usr/bin/curl -> 142.250.206.206:80 (tcp)
+```
+
+
+
+Python 뷰어 스크립트를 통해 출력된 결과값은 필요한 정보만 확인할 수 있어 매우 가독성을 높혀준다.
+
+Ubuntu 24.04 에서 각각의 명령어의 Hash(SHA256) 값을 확인 할 수 있다.
+
+#### ssh
+
+- SHA256=ffc312252dd2756e3346f1fd5a680486a4073abef452d44939351742d9b4bba0
+
+#### curl
+
+- SHA256=aca992dba6da014cd5baaa739624e68362c8930337f3a547114afdbd708d06a4
+
+#### wget
+
+- SHA256=b8f8a975cf3e7908e076de79814aa448c6086aacfc08165be04ce65665e08e39
+
+### References
+
+[1] [https://github.com/microsoft/SysmonForLinux.git](https://github.com/microsoft/SysmonForLinux.git?ref=openpesto.com)
diff --git a/content/writing/how-to-win-best-paper-ko.md b/content/writing/how-to-win-best-paper-ko.md
@@ -0,0 +1,255 @@
+---
+title: "논문 작성 방법론"
+date: 2026-09-08
+tags: []
+---
+
+**Nicholas Carlini** | 2026년 3월 9일
+
+*원문: [How to win a best paper award (or, an opinionated take on how to do important research)](https://nicholas.carlini.com/writing/2026/how-to-win-a-best-paper-award.html)*
+
+---
+
+# 1. 좋은 연구 아이디어 구상하기
+
+## Have good taste for problems
+
+영향력 높은 연구를 수행하기 위해 길러야 할 단 하나의 가장 중요한 기술은 **어떤 문제가 해결할 가지가 있는가**를 알아보는 좋은 안목(Taste)이다.
+
+뛰어난 안목을 개발한 연구자들은 기발하고 우아한 접근 방식을 찾아내며 **‘직관적으로 옳다’**고 느겨지는 해결책으로 끌린다. 거시적인 차원에서 그들은 **미래에 중요해질 문제**를 선택하고, 미시적인 차원에서는 **성공 가능성이 높은 접근법**을 택한다. 그리고 수개월의 노력을 낭비하기 전에 이러한 결정을 초기에 내린다.
+
+반면 안목을 가다듬지 못한 사람들은 중요하지 않은 문제에 수개월을 낭비하거나, 처음부터 실패할 수밖에 없는 접근 방식을 취한다. 그들은 기술적으로는 완벽할지 모르나 아무도 읽지 않는 논문을 쓴다. 왜냐하면 **그 질문이 정말로 답할 가지가 있는 질문**인지 애초에 묻지 않았기 때문이다.
+
+**안목이란 연구라는 행위를 계속 실천하면서, 항상 ‘무엇이 작동하고 무엇이 작동하지 않는지’를 치열하게 식별해 내는 과정**에서 온다.
+
+## Have great collaborators
+
+아무도 고립된 상태에서 과학을 연구하지 않는다. 무언가를 이루어내는 유일한 방법은 **다른 사람들의 아이디어를 바탕으로 한 층 더 발전**시키는 것이며, 이때 함께 일할 수 있는 **동료**가 있다는 것은 엄청난 도움이 된다.
+
+좋은 공동 연구자는 단순히 업무를 분담해 주는 수준을 넘어선다. 그들은 우리가 범한 오류를 남들이 발견하기 전에 먼저 잡아준다. 헛수고를 하지 않도록 잘못된 아이디어에 제동을 걸어준다. 내가 가지지 못한 스킬을 불어넣어 주고, 애초에 무엇을 먼저 연구해야 할지 고민할 때 아이디어를 주고받을 수 있는 든든한 상대가 되어 준다.
+
+많은 사람들이 누군가 자신의 아이디어를 훔쳐 갈까 봐 아이디어를 꽁꽁 숨겨둔다. 그럴 필요가 전혀 없다. **아이디어는 저렴하며, 진짜 어려운 것은 실행이다.** 대부분의 연구자들은 이미 연구할 시간이 모자랄 정도로 아이디어가 차고 넘친다. 따라서 다른 사람이 하던 일을 모두 제쳐두고 우리들의 아이디어를 가로챌 가능성은 극히 희박하다. 설령 가로채려고 하더라도, 오랫동안 그 고민을 성숙시켜 온 우리들만큼 훌륭하게 실행해 내지 못할 것이다.
+
+## Read all the papers
+
+지금 함께 일하고 있지 않은 세상의 모든 다른 이들에게 배워야 한다.
+
+연구를 출판하는 유일한 이유는 과학 커뮤니티에 새로운 지식을 가르치기 위함이다. 따라서 전 세계 연구자들의 지혜를 모으는데 필요한 전부는 문헌(Literature)을 가능한 한 많이 읽는 것이다. 기존의 과학적 지식이 어디까지 도달했는지 모른다면 그 위에 해로운 지식을 쌓아 올릴 수 없다.
+
+논문을 읽을때는 **왜 이 논문을 읽는지** 그 의도를 명확히 하고, 다음 세 가지 마인드셋 중 하나를 취한다.
+
+- **무엇이 가능한지 파악하기 위해 (To get a sense of what is possible)**: 대부분의 논문은 **최신 트랜드를 파악**하기 위한 가벼운 독서이다. 이를 통해 얻고자 하는 것은 단 두 가지이다. **(1) 이 논문이 유용한가? 만약 그렇다면 (2) 이 논문을 유용하게 만드는 핵심적인 새로움은 무엇인가?** 잘 쓰인 논문이라면 이 질문에 대한 답이 한 문장으로 요약될 수 있어야 한다. 독자로서의 목표는 그 한 문장을 찾아내는 것이다. (반대로 작성자로서의 임무는 독자가 그 한 문장을 쉽게 찾을 수 있도록 돕는 것이다.
+- **어떤 결과를 얻기 위해 무엇을 해야 하는지 이해하기 위해 (To understand what you have to do to achieve some result)**: 본인의 연구와 직접적인 관련이 있는 논문이라면 주의 깊게 읽어야 한다. 세세한 세부 사항까지 전부 다 이해할 필요는 없지만, 필요한 부분만큼은 깊이 파고들어 완벽히 이해해야 한다. 예를들어 작성자가 사용한 기발한 증명 기법, 무척 우아한 실험 셋업, 혹은 내가 가져다 쓸 새로운 알고리즘 등이 있다. **주변부의 세세한 이야기에 얽매이지 않고 필요한 애센스만 추출해 낸다.**
+- **해당 논문을 확장하기 위해 (To extend upon the paper)**: 극히 소수의 논문(한 달에 몇 편 정도)에 한해서는, 첫 페이지부터 마지막 페이지까지 글자 그대로 정독하며 **모든 문장을 비판적으로 검토**한다. 이 수준에서는 논문을 다시 보지 않고도 머릿속으로 전체 연구를 완벽히 재현할 수 있을 만큼 깊이 이해하는 것이 목표이다. 이를 통해 다음과 같은 질문의 답을 찾는다. *그들이 암묵적으로 내린 가정은 무엇인가? 향후 연구 과제로 남겨둔 빈틈은 무엇인가? 그들이 저지른 논리적 오류는 없는가?*
+
+**제대로 이해하지 못한 대상은 고칠 수도 없다.**
+
+## Ignore all the papers
+
+모든 것을 읽고 난 후 취해야 두 번째 단계는 **그 모든 것을 전부 잊어버리는 것**이다. 이미 세상에 존재하고 수행된 일은 더 이상 새로운 것이 없다. **기존에 기대된 방식에 갇혀 생각한다면, 기발하고 참신한 무언가를 결코 생각해 낼 수 없다.**
+
+**잘못된 아이디어에 휩쓸리지 않는 것**이 매우 중요하다. 연구 분야를 막론하고 **어디나 잘못된 아이디어는 존재한다.** 어떤 인지도 높은 저명한 인물이 접근법, 방법론, 혹은 평가 지표에 아주 치명적인 실수를 담은 영향력 있는 논문을 발표하고 나면, 다른 연구자들은 최초의 이루어진 설계에 대해 아무런 비판적 성찰도 없이 잘못된 길을 그대로 따르게 된다.
+
+또 다른 경우로, 연구 분야가 태동하는 초기 단계에 빠른 출판을 위해 엄격한 타당성 검증을 거치지 않은 임의의 결정이 논문에 포함될 때가 있다. 그러면 후속 연구자들이 무비판적으로 그 조약한 설계를 지나치게 오랫동안 표준처럼 수용하는 비극이 발생한다. 학계가 일해온 방식에 너무 많은 신경을 쓰다 보면, 본인도 모르게 이 잘못된 접근들을 정답으로 내면화하게 된다.
+
+문헌을 의도적으로 무시하는 것이 도움이 되는 또 다른 이유는, 수많은 논문이 해결하고자 매달린 난제가 존재할 때 사람들은 단순히 ‘아무도 해결하지 못했다’는 이유만으로 그것이 엄청나게 어려운 문제일 것이라 미리 짐작하기 때문이다. 정작 누구도 근본적으로 다른 페러다임으로 접근해 본 적이 없다.
+
+결정적인 실패 원인 중 하나는, 한 번 눈에 들어온 잘못된 아이디어가 머릿속을 독점하는 현상이다. 같은 접근법 X를 적용한 논문을 10개쯤 보고 나면, 굳이 의식하지 않아도 “방법 X가 맞다”고 지레 짐작해 버리게 된다.
+
+## Pick your ideas for impact
+
+어떤 연구자들은 단순히 ‘학회에 논문 한 편 제출하기’를 목표로 연구를 시작한다. 절대 그렇게 하면 안된다. 연구에 임할 때는 항상 **무언가 흥미롭고, 중요하며, 완전히 새로운 진실을 발견하겠다**는 목적을 가져야 한다. 논문은 그 발견의 당연하고 자연스러운 부산물일 뿐이다.
+
+**대충 평범하게 타협하려는 목표에서는 절대 엄청난 영향력(High Impact)을 가진 연구가 나올 수 없다.** *“기술적인 요건을 겨우 채워서 논문 취급을 받을 수 있는 최소한의 기여(Minimal Contribution)가 뭘까?”*라고 스스로에게 질문을 던지기 시작하는 순간, 대단한 가치를 지닌 발견을 할 기회를 잃게 된다. 또는, 그 보잘것없는 목표를 완벽하게 달성해 내어, 아무런 영혼도 없고 존재할 이유도 없는 평범한 논문 한편을 세상에 추가하게 될 것이다.
+
+(조금 씁슬한 현실이지만, 정말 훌륭하고 독창적인 발견을 한 논문보다, 대세에 슬쩍 묻어가며 적당히 구색만 갖춘 지루한 논문들이 학회에 더 쉽게 Accept되기도 한다. 그러나 그런 식으로 자신의 대표작이 될 만한 중요한 연구를 남길 수는 없다. 거절당하더라도 단단하게 버텨내는 강한 멘탈을 일찍 길러야 한다.)
+
+컴퓨터 과학자 Richard Hamming은 주변 연구자들에게 늘 이런 뼈 때리는 질문을 던진 것으로 유명하다. **“당신의 분야에서 가장 중요한 문제가 무엇입니까” 그리고 당신은 왜 그 문제를 연구하고 있지 않습니까”** 연구에 임할 때 항상 이 마인드셋을 가져야한다. 진정으로 훌륭한 논문 한 편이 영혼 없는 평범한 논문 천 편보다 가치 있으며, 아이러니하게도 집필하는 데 걸리는 시간마저 더 적게 든다.
+
+그렇다고 우리가 쓰는 모든 논문이 반드시 세상을 뒤흔들 거대한 야망을 품어야 한다는 뜻은 아니다. 가끔은 무엇이 정말 중요한 연구인지 갈피를 잡기 어려울 때도 있다. 아이디어를 고민하느라 흘려보내는 한 시간의 가치보다. 그냥 무엇이든 실제로 붙잡고 구현해 보는 노동의 가치가 더 클 때가 많다. Nicholas Carlini는 매년 본인이 생각하기에 엄청난 잠재력을 지닌 에이스급 논문을 한두 편 정도 쓰고, 나머지 논문들은 본인의 감각과 연마된 기술을 유지하기 위해 작성한다. ‘적당히 훌륭한’논문들을 끊임없이 써나가는 손끝의 훈련 과정에서, 학계를 바꿀 핵심 아이디어가 튀어나오기 때문이다.
+
+## Do something only you can do
+
+오직 본인만이 할 수 있는 일을 시도해야 한다. 다른 누구도 똑같은 학회에 제출하지 않을 것 같은 독특한 궤적의 논문을 구상해야 한다.
+
+대부분의 연구자들은 남들보다 고작 몇 달 먼저 미래의 장막을 걷어내는 선에서 경쟁한다. 따라서 우리가 학계에 한 기여의 실질적인 가치는, **우리가 오늘 발표한 시점과, 다른 이가 그것을 독자적으로 깨닫고 발표했을 미래 시점 사이의 시간적 차이**로 정의할 수 있다. 내가 아닌 다른 사람이 하려고 했다면 적어도 나와 동등한 수준의 완성도로 구현하는 데 최소 몇 개월 이상은 끙끙 앓아야 했을 만한 가치 있는 주제를 골라야 한다.
+
+만약 우리가 훌륭한 퀄리티의 연구를 한발 먼저 세상에 내놓은다면, 그 똑같은 논문을 1년 뒤에 게재하는 것보다 휠씬 엄청난 영향력을 가진다. 사람들은 초기에 정해진 패러다임(How things are done)을 빠르게 수용하는 경향이 있어, 한 번 굳어진 학계의 흐름을 교정하는 것은 처음부터 올바른 길로 학계를 이끄는 것보다 수십 배는 어렵기 때문이다. 분야가 태동하는 초기시점에 6개월 일찍 작성한 정확한 지표와 프레임워크가 커뮤니티의 방향성을 정립하는 데 독보적인 역할을 하는 이유가 여기에 있다.
+
+**오직 본인만이 할 수 있는 핵심 문제를 잡았는지 확신하는 방법은 다음과 같다. 기존 연구들을 읽을 때 속에서 울화통이 치밀어 오르는 영역을 주로 살펴본다. *“왜 다들 이렇게 눈에 뻔히 보이는 잘못된 방식으로 일하고 있는 거지?”* 하며 소리를 지르고 싶은 바로 그 지점이다.**
+
+## Find your comparative advantage
+
+나만의 독보적인 장점과 비교 우위가 정확히 어떤 차원(dimension)에 걸쳐 있는지 파악하는 것도 매우 유익하다. 필즈상 수상자인 수학자 티모시 가워스(Timothy Gowers)는 수학자가 크게 **‘이론 설계자(Theory-builder)’와 ‘문제 해결사(Problem-solver)’**의 두 가지 분류로 나뉜다고 하였다. 전자는 수학이 작동하는 체계적인 구조와 이론적 뼈대를 만드는 데 매료되는 반면, 후자는 눈앞에 닥친 구체적인 명제를 증명하거나 파괴하는 일 자체에 쾌감을 느낀다. 두 유형 모두 학문의 발전에 필수적이지만, 자신이 어떤 역할에 더 능숙한지는 사람마다 완전히 다르다.
+
+역량을 발휘할 수 있는 차원은 이 두 가지만 있는 것이 아니며, 세부 연구 도메인에 따라 매우 복잡하고 고차원적으로 뻗어 있다. 그 차원들이 워낙 다양하기 때문에, **우리가 가진 고유한 강점의 조합이 전 세계에서 특정 지점만큼은 가장 강력한 무기가 될 확률이 무척 높다.** 나만의 구석(Corner)을 찾아야 한다.
+
+**스스로 어떤 고유한 비교 우위를 지니고 있는지 깊이 성찰해야 한다**. 다른 이들이 어려워하는 장점들을 결합할 수 있는 세 갈래 혹은 네 갈래의 고유한 장점이 반드시 존재한다. 모든 연구가 반드시 내 장점의 극점에 맞닿아 있을 필요는 없지만, 나의 장점과 궁합이 정확히 맞아떨어지는 천혜의 기회가 보인다면 절대 놓치면 안된다.
+
+## Get lucky: you happen upon a good idea
+
+그 무엇보다도, 본질적인 핵심은 결국 운이 좋아야 한다.
+
+사전에 길의 끝을 아는 것은 불가능하며, 그저 본인이 내린 선택이 정답이기를 바랄 뿐이다. (이때 좋은 ‘연구적 안목’을 갖추고 있다면 본격적인 탐험에 앞서 그 길이 얼마나 비옥할지 더 놓은 확률로 예측하는 보조 역할을 해준다.)
+
+아무리 성실하게 모든 것을 완벽하게 수행하더라도, 본인이 통제할 수 없는 수많은 변수로 인해 번뜩이는 돌파구를 만나지 못할 수도 있다.
+
+모든 것이 100% 운이라는 뜻은 아니다. 다만 모든 과정에 행운의 영역이 있음을 부인할 수는 없다. 운이 마침내 나를 찾아왔을 때 그것을 낚아채서 내 것으로 만드는 순발력 도한 부단한 수련을 통해서만 얻을 수 있다.
+
+## Get lucky: you’re well suited to solve important problems
+
+학술 연구 세계에서는 개개인의 총명함을 떠나 그 시대를 관통하는 거대한 과학적 합의의 성숙도에 따라 특정 연구 도메인의 중요도가 천차만별로 널뛰기를 한다.
+
+이 냉혹한 현실이 시사하는 바는 두 가지이다.
+
+1. 불행하게도 만약 본인이 심취해 있고 아주 뛰어난 재능을 가진 고유한 영역이 동시대 학계로부터 주목을 받지 못한다면, 세간의 인정을 받는 데 무척 고단한 가시밭길을 걸을 것이다.
+2. 그러나 다행히도 만약 본인이 남들이 우르르 몰려가는 과열된 트렌드가 아닌 조용한 자기만의 우물(Niche)을 파고 있다면, 독보적으로 두각을 드러내어 살아남기가 훨씬 유리하다.
+
+그렇다고 당장 주목을 받지 못하는 소외된 일을 완전히 내팽개쳐야 한다는 것은 아니다. 개인의 학술적 영향력의 총합이란 **내가 가진 실력의 깊이 X 그 연구 분야가 당대에 갖는 본질적인 임무의 크기”**의 곱으로 표현된다. 본인이 압도적인 1인자가 디어 혁신을 주도할 수 있는 조금 작은 분야에서 일하는 것이, 본인에게 전혀 어울리지도 않고 잘하지도 못하는 대세 분야에서 들러리를 서며 평범한 성과를 내는 것보다 인류의 문명을 전진시키는 데 훨씬 크게 기여하는 법이다.
+
+(그런 점에서 진짜 위대한 연구자들은, 한 분야에서 기념비적인 족적을 남긴 뒤 기술의 조류가 바뀌었을 때 완전히 자기 파괴적인 변신(Re-invent)을 단행하여, 전혀 새로운 영역으로 넘어가 그곳의 핵심 문제마저 완벽하게 격파해 내는 영웅들이다.)
+
+# 2. 훌륭한 기술 연구 수행하기
+
+## Get lucky
+
+**어떤 가설이 진실로 참이고 어떤 기법이 작동할지는 인간이 사전에 통제하거나 확신할 수 없으며(이 불확실성이야말로 엔지니어링과 과학을 구분짓는 가장 고귀한 경계선이다)**, 우리가 수립한 아름다운 이론이 실제 실험에서도 잘 작동해 주기를 겸허히 기도하는 수밖에 없다.
+
+이 과정에서 멘탈을 지키는 가장 현명한 태도는 **내가낸 아이디어의 대다수는 실패할 수밖에 없다**는 당연한 과학적 섭리를 차분하게 내면화하는 것이다. 온 힘을 다해 가설을 꼼꼼하게 검증하는 노고는 바칠 수 있으나, 인간의 치열한 노력이 수학적으로 거짓인 명제를 참으로 바꾸어 줄 수는 없다.
+
+## Kill papers that are not working
+
+어떤 아이디어가 살아남고 죽을지가 거대한 행운의 장난이라면, 우리가 취할 수 있는 현실적인 전략은 명확하다. 진짜 진주 같은 오아이스를 만날 때까지 **수많은 탐색 카드를 쥐고 끊임없이 던지는 것**이다. 모든 진정성 있는 훌륭한 발견에는 필연적으로 가설이 틀릴 수 있는 거대한 위험 부담(Risk)이 도사리고 있다. 따라서 최대한 많은 경우의 수를 굴려 보아야 하며, 이를 수행하기 위한 가장 강력한 전제 조건은 **풀리지 않는 오답 카드를 무자비하게 폐기하는 것**이다.
+
+아까운 시간을 낭비하는 늪에서 가장 신속하게 빠져나오는 요령은, **연구 아이디어가 가진 핵심적인 가설 중 ‘가장 높은 확률로 무너질 것 같은 아킬레스건’을 첫 주자로 실험대에 올리는 것**이다.
+
+인간은 본성적으로 자신이 이미 구현할 줄 아는 쉬운 파트나 깔끔하게 잘 굴러가는 곁가지 모듈을 코딩하면서 안도감을 얻기 마련이다. 하지만 그런 안온한 코딩 작업은 내 아이디어가 진짜 참인지 거짓인지에 대해 일말의 힌트도 주지 못한다. **핵심적인 허들을 가장 맨 앞에 배치하여, 이 프로젝트가 과연 생존할 수 있는지(De-risk)를 극초기에 가장 빠른 속도로 담판 지어야 한다.**
+
+논문을 빌딩하는 과정은 신생 스타트업을 창업해 서비스를 개발하는 과정과 매우 닮았다. 비즈니스 세계의 격언인 **“빠르게 실패하라(Fail Fait)”**는 연구자의 랩실에서도 완벽한 진리이다. 전체 시스템의 세련된 프로덕션을 정성 들여 깎기 전에, 이 핵심 가설이 작동하는지를 판단할 수 있는 가장 투박한 프로토타입(Prototype)을 빠르게 얹어 검증해야한다. 만약 프로토타입이 훌륭한 빛을 보인다면 디테일과 아키텍처는 나중에 우아하게 다듬으면 그만이다.
+
+## Kill papers that end up having low impact
+
+자신의 연구가 최종 게제되었을 때 학계에 신선한 파장을 던질 만한 파괴력(Impact)은 애초에 없어보이고 적당히 수치를 포장하고 억지로 문장력을 동원에 살을 붙인다면, 어떻게든 학회 승인(Accept) 턱걸이는 넘을 법한 구색을 갖춘 논문 한 편을 찍어낼 수는 있다.
+
+하지만 그것은 절대로 좋은 논문이 될 수 없다. 이 순간이 찾아오면 정성과 매몰 비용에 미련을 두지 말고 **해당 아이디어를 단호하게 폐기 처분(Kill)하자.** 도저히 버리기 아깝다면 가벼운 워크숍(Workshop) 제출용이나 개인 블로그 포스팅으로 핵심 노하우만 툭 털어 정산해 버려랴. 어떻게든 그 실패의 과정에서도 작은 학습의 교훈을 취하되, 내 평생의 중요한 학술적 자산이 될 핵심 연구 일정을 구차하게 타협하는 지루한 글쓰기에 더 이상 금쪽같은 에너지를 낭비해서는 안된다.
+
+## Re-prioritize projects ruthlessly
+
+지금 굴리고 있는 연구 노선에 아무런 기술적 하자가 없고 순탄하게 굴러가고 있음에도 불구하고, 갑자기 훨씬 더 압도적으로 임팩트를 품은 대형 아이디어가 머리를 내려칠 대가 있다. 이런 순간을 마주하면, 기존 프로젝트의 진척도에 억매이는 ‘매몰 비용 오류(Sunk cost Fallacy)’에 단 1초도 사로잡히면 안된다. **즉시 모든 리소스를 중단하고 더 위대한 연구로 거침없이 피벗(Pivot)해야 한다.**
+
+**연구의 가치와 파급력의 분포는 선형적인 그래프가 아닌 ‘지수 분포(Exponential Distribution)’의 법칙을 따른다.** 아무리 평범하게 90% 정도의 완성을 눈앞에 둔 ‘적당히 쓸 만한’ 연구가 있더라도, 마지막 10%의 힘을 쥐어짜 내 마무리 짓는 기회비용보다, 그 에너지를 고스란히 배내어 100배는 더 가치 있고 세상을 바꿀 파괴적인 최우선 아이디어의 첫 단추를 꿰는 데 투입하는 것이 수학적으로 비교 불가할 정도로 이득이다. 실제로 세상의 어떤 논문들은 평이한 논문 천 편보다 100배 이상 중요하다.
+
+이 내용을 현실에 적용할 때는 **상당한 균형 감각과 경계심을 지녀야 한다.** 우선, 한 편의 논문이 다 완성되기도 전에 장차 세상에 미칠 실질적인 가치를 정확하게 예견하다는 것 자체가 극도로 어려운 영역이다. 게다가 **안목이 아무리 예리하더라도 다른 사람의 새 아이디어가 본질적으로 훨씬 더 매력적이게 보이기 마련이다.** 단순히 과정이 지겹고 힘들어져서 번지르르한 새 떡밥으로 도망치는 **‘지겨움의 도피성 피벗’을 스스로 혁신적인 결단이라고 정당화하는 자기기만에 빠지면 안된다.**
+
+## Put in an unreasonable amount of effort
+
+> “가금 마법이란, 단지 어떤 사람이 다른 평범한 이들이 상식적으로 예상할 수 없을 만큼 무지막지하게 오랜 시간과 정성을 한 지점에 때려 부울 때 일어나는 현상일 뿐입니다.”
+— 세계적인 마술사 듀오 펜 앤 텔러(Penn & Teller)의 텔러(Teller)
+>
+
+위대한 논문에는 단 하나의 허접하거나 대충 타협한 조잡한 실험도 섞여 있지 않다. 높은 품격의 과학적 결과물을 빚어낸다는 것은, 세련된 핵심 아이디어를 구상했다는 찬사뿐만 아니라, **보통의 평범한 이들이라면 “왜 저 짓거리까지 하고 자빠졌지?”라고 혀를 내두를 정도로 터무니없는 극한의 한계까지 집요하게 물고 늘어져 실험을 완벽하게 밀어붙였다는 신뢰**를 의미한다.
+
+수행하는 모든 작은 비교 실험 하나하나에도 기계적인 정밀함과 영혼을 담아야 한다. 표본의 오차를 없애기 위해 수십 번의 교차 검증을 마다하지 말고, 인과 관계를 왜곡하는 숨겨진 교란 변수(Confounders)가 없는지 치열하게 의심해야한다. 내 논문을 잔뜩 날이 선 채로 깎아내리며 뜯어볼 무척 까다롭고 시니컬한 동료 리뷰어가 던질 만한 날카로운 질문들을 미리 브레인스토밍해 두고, 그들이 공식적으로 질문을 전지기도 전에 서론과 실험 섹션에 완벽한 대조 실험 데이터로 입을 막아버려라. 실험 결과가 ‘대체로 작동하는 편(Sometimes)’ 수준에 머무르는 것을 ‘압도적인 빈도로 견고하게 작동함(Usually)’으로 끌어올려야 한다. 과학을 진심으로 대한다는 것은 바로 그러한 집요한 디테일을 가꾸는 마음가짐을 뜻하기 때문이다.
+
+## Have focus
+
+논문의 유일무이한 가치는 **단 하나의 명확한 핵심 아이디어를 학계에 완벽히 이식하는 것**을 잊으면 안된다. 이 핵심 메시지는 다양한 실험과 다채로운 보조 장치를 통해 묘사되지만, 종국에는 단 한줄의 압축적인 요약문으로 입증될 수 있어야 한다. 본문을 워드로 본격 작성하기 전부터, 이 연구를 통해 달성하고자 하는 단 하나의 선명한 정의를 메모지에 적어 모니터 옆에 붙여둔다. **글을 쓰는 기나긴 터널 속에서 길을 잃거나 욕심을 부려 곁가지로 빠지는 참사를 원천 차단**하기 위함이다.
+
+본문에 들어가는 모든 실험 데이터, 모든 문단, 심지어 삽입된 모든 그림 한 장 한 장까지 예외 없이 오직 **그 단 하나의 타깃을 저격하기 위한 일관된 논거**로 정렬되어 있어야 한다.
+
+우리가 수행하는 과학적 발견의 본질은 우주와 세상의 가려진 진실의 베일을 한 꺼풀 벗겨내는 숭고한 행위이므로, 열성적인 연구자일수록 새 도구를 쥐었을 때 해볼 수 있는 온갖 재미있는 파생 실험들을 끝도 없이 구현해 보려는 유혹에 눈이 멀기 쉽ㄴ다. 그러나 이는 치명적인 함정이다.
+
+1. 세상 모든 가벼운 궁금증을 다 테스트해 보려고 들다간 연구 기간이 한도 끝도 없이 늘어져 정작 본문을 다 완성하기도 전에 지쳐서 고사할 것이다.
+2. 산만하게 널브러진 잡다한 실험 데이터들은 독자들의 시선을 분산시켜, 정작 우리가 증명하고자 했던 위대한 기적의 한 점을 흐릿하게 지워버린다. 간혹 까다로운 심사위원의 억지 요청으로 어쩔 수 없이 곁가지 주변 실험들을 추가해야 만 할 때가 생기더라도, 본문 구석으로 조심히 밀어 넣고 문맥의 헤드라이트는 언제나 중심 주제로 재빠르게 돌려놓는 기민함을 발휘해야 한다.
+
+## The paper should be the “maximal” version of the paper
+
+‘단 하나의 뾰족한 집중’이라는 원칙과 절묘한 균형을 맞추기 위해 명심해야 할 도 다른 기둥이 있다. 내가 내놓는 논문이 **“누가 봐도 이 주제를 다루었다면 뻔히 수정했어야 할 당연한 후속 조치나 상식적인 보완책”**을 게으름 때문에 누락한 채 방치되어 있어서는 안 된다는 점이다. 즉, 논문은 최소한 그 좁혀진 도메인 안에서만큼은 단 하나의 확실한 종결을 짓는 로컬 최적점(Local Optimum)에 확실히 안착해 있어야 한다. 논문을 다 읽은 독자의 머릿속에 *“아, 다 좋은데 왜 가장 핵심적인 실험 X는 빼먹고 안 했지? 그거 해봤으면 바로 답 나왔을 텐데…”*하는 찝찝한 아쉬움이 스치게 만들어서는 안 된다.
+
+예를들어 본인이 제안하는 획기적인 최신 프레임워크가 있다면, 그것을 해당 도메인 X의 가장 표준적이고 까다로운 벤치마크 데이터에 대입해 정면 승부하는 것을 회피해서는 안 된다. 내 수식 결과가 내포하는 너무나 자명하고 흥미로운 따른정리(Lemma)들이 있다면 꼼꼼하게 기술해 두어야 한다. 누구나 논문을 보자마자 합리적으로 제기할 법한 치명적인 한계점이나 반론이 예상된다면, 비겁하게 모른 척 숨기지 말고 본문 한 편에 당당하게 지면을 할애해 정면으로 해명하고 극복 가능성을 제시해야 한다.
+
+학술적인 맥락에서 **사소하지만 매력적인 후속 개선 과제들을 독자들의 몫으로 예쁘게 남겨두는 것**은 대단히 긍정적인 신호이다. 내 논문을 읽고 깊은 영감을 받은 전 세계의 후속 연구자들이 우리가 열어둔 빈틈을 탐험하기 위해 우리의 깃허브 코드를 다운받고 논문을 인용하며 메일로 질문을 던지기 시작할 것이다. 그들이 뛰어놀 수 있는 빈 놀이터를 조금 남겨둠으로써, 우리가 개척한 신대륙의 생태계가 더욱 거대하게 번성하는 기틀을 닦는 셈이다.
+
+# 3. 이해하기 쉽고 설득력 있는 논문 작성하기
+
+본인이 제안한 기술이 아무리 수학적으로 경이롭고 복잡할지라도, 후속 연구자들이 그 원리를 친절하게 이해하고 자신의 코드에 이식할 수 있도록 돕는 **가장 쉬운 진입 장벽과 압도적인 가독성을 제공**하지 못한다면 해당 연구는 쓸쓸히 잊힐 것이다. 기술의 복잡도는 어차피 수년 내에 더 영리한 누군가에 의해 완벽하게 개선되어 대체될 운명이지만, **한 분야의 초입을 가장 아릅답고 직관적으로 설명해 낸 불멸의 교과서 같은 접근성을 지닌 논문은 영원히 마스터피스로 남아 두고도구 인용되는 힘을 갖는다.**
+
+논문의 수식 자체가 아닌, 그 수식을 설명하는 인간적인 가독성과 친근한 문장력이 논문에 영원한 생명력을 불어넣어 준다.
+
+## Have focus
+
+우수한 과학 논문은 언제나 **‘오직 단 하나의 핵심 아이디어(One Singular Idea)’**만을 가리키고 있어야 한다. 이 독창적인 한 점을 가장 강력하게 호위하기 위해 다채로운 보조 실험들을 배치할 수도 있고, 풍성한 외연 확장을 시도할 수도 있다. 하지만 주어가 되는 핵심 코어는 언제나 단 하나여야 한다. 본문의 아주 사소한 배경지식(Background) 설명 한 칸조차도, 결국 이 핵심 아이디어를 완벽히 납득시키기 위한 사전 빌드업 작업에 철저히 기여하고 있어야 한다.
+
+연구를 하다 보면 온갖 신기하고 다양한 인사이트와 잔가지 아이디어들을 한꺼번에 잔뜩 발견해 논문 구석구석에 자랑스럽게 늘어놓고 싶은 욕심이 솟구친다. 이때 **치열한 절제력으로 중심축 외의 모든 잡념을 과감하게 쳐내야 한다**. 동시에 세 개, 네 개의 화려한 주제를 한 논문에 우겨넣어 설명하려는 과욕을 부리는 순간, 독자는 책을 덮을 대 그 어떤 단 하나의 단어도 머릿속에 남기지 못하고 전부 잊어버리게 될 것이다.
+
+## Know your reader
+
+**타깃이 될 단 하나의 명확한 가상의 독자 페르소나**를 날카롭게 정의해 두고, 그 사람의 눈높이에 맞추어 본문을 정밀 서술해야 한다.
+
+이를 통해 가장 설득력 있는 서론(독자가 내 연구에 매료되기 위해 어떤 문제의식부터 자극해야 하는가), 명확한 배경지식(독자가 내 방법론을 완벽히 소화하기 위해 미리 탑재하고 있어야 할 필수적인 기본기가 무엇인가), 본문 방법론(독자가 이미 익숙하게 알고 있는 기술적 유산 위에서 내 신기술이 어떻게 우아하게 얹어지는가), 감동적인 결론(독자가 일상으로 돌아갈 때 가슴속에 품고 가야 할 단 하나의 중요한 울림이 무엇인가)을 완벽하게 기획할 수 있다.
+
+(참고로, 배경지식(Background) 섹션의 진정한 학술적 존재 의의는 **‘내 논문을 이해할 수 있는 독자의 진입 장벽을 극적으로 확장해 주는 사다리’** 역할을 수행하는 데 있다. 처음 서론을 읽을 때는 해당 분야의 문외한일지라도, 정성껏 설계된 배경지식 문단을 마스터하고 나면 바로 다음장의 고급 방법론을 막힘없이 소화할 수 잇는 든든한 전문가로 무장시키는 단계여야 한다.
+
+가상의 독자를 설정할 때 성공적인 최적의 타깃은 **‘6개월 전 과거의 내 자신’**이다. 6개월 전의 헤매던 나 자신에게 이 원리를 가장 쉽고 세련되게 과외해 주려면 어디서부터 어떻게 말문을 열어야 할까? 왜 이 구조가 수많은 막다른 골목을 뚫어낸 유일무이한 최종 열쇠였는지를 과거의 나에게 가장 친절하게 납득시키는 상상을 하며 글을 쓰는 것이다.
+
+내 논문이 특정 진영의 잘못된 고정관념을 정면으로 타파하고 그들의 고집스러운 생각을 뜯어고치기 위한 강력한 논쟁의 도구로 기획되었을 경우에는 **철저하게 반대 진영의 뇌 구조와 논리를 시뮬레이션**하며 글을 써야 한다.
+
+## Don’t obsess over your title
+
+**본문이 압도적으로 훌륭하다면 논문의 제목은 어떻게 짓든 아무런 상관이 없다.** 현대 온갖 자극적인 클릭베이트(Clickbait, 어그로성 낚시글)가 판치는 피로한 세상 속에서, 감사하게도 우리 과학 생태계는 제목이 그거 담백하고 정직하게 내용물만 가리키고 있다면 아무런 패널티를 주지 않는 품격 있는 정직한 동네이다. 우리의 엄숙한 소명은 위대한 과학적 진실을 탐구하는 것이지, 그저 실체 없는 피인용 수(Citation)을 늘리기 위해 과장되고 광범위한 키워드를 억지로 짜집기해 학계에 어그로를 끄는 저렴한 마케터가 아니다.
+
+**제목은 내 논문의 알맹이가 무엇인지 일체의 왜곡 없이 성실하고 명확하게 대변해 주어야 한다.**
+
+만약 논문의 제목을 어떻게 지어야 할지 갈피를 잡지 못한다면 작성 중인 논문이 단 하나의 명확한 골조에 집중하지 못하고 지나치게 이것저것 걸치고 있다는 부작용의 치명적인 적색 신호이다.
+
+## Your abstract does matter
+
+제목은 대충 지어도 무방하지만, **초록(Abstract)만큼은 한 자 한 자 영혼을 실어 정밀하게 작성해야 한다.**
+
+초록의 존재 의의는 단 다섯 줄 내외의 짧은 정제된 문장만으로, (1) 논문 전체의 방대한 물리적 시공간을 완벽히 압축해 전달하는 동시에, (2) 바쁜 현대 연구자들의 마음을 한눈에 사로잡아 *”이 논문은 무조건 출력해서 정독해야겠다”*라는 강력한 지적 흥분과 소유욕을 유발하는 데 있다. 이것은 인간의 글쓰기 예술 중 가장 난이도가 높은 초고난도의 영역이다.
+
+Nicholas Carlini가 정의한 초록의 5문장 뼈대(Formula)는 다음과 같다.
+
+- 제1문장: 어떤 주제를 다루고 있는지 밝힌다.
+- 제2문장: 해당 분야에서 어떤 문제를 해결할 것인지 설명한다.
+- 제3문장: 연구 결과나 방법론에 대해 언급한다.
+- 제4문장: 제3문장에서 다루지 않는 내용을 다룬다.
+- 제5문장: 이 연구가 왜 중요한지에 대해 설명한다.
+
+두 번째로 자주 사용하는 방식은 (1) 새로운 결과에 대한 주장, (2) 이 주장을 뒷받침하는 근거(예: 방법론이나 데이터), (3) 이 결과에 영향력 순으로 진행한다. 이 방식은 주제가 이미 매우 광범위하여(따라서 주제를 명시하는 것이 중요하지 않은 경우)나, 논문이 매우 특정한 독자층만을 대상으로 할 때 사용하는 방법이다.
+
+초록은 구체적이어야 하며, 논문을 통해 무엇을 얻을 수 있는지 정확하게 설명해야 한다. 말을 돌려서 표현하지 말고, 결과에 대한 명확하고(사실에 입각한) 내용을 그대로 기술해야 한다.
+
+초록은 논문의 중요성을 전달해야 한다. 이는 선택한 문제, 개발한 새로운 방법론, 혹은 독자들이 얻어가길 바라는 중요한 교훈 등일 수 있다. 자신이 중요하다고 생각하는 바를 명확히 드러내고, 그 점에 집중해야 한다.
+
+## Write a good introduction: you're telling a story
+
+서론은 이야기의 시작이다. 먼저 독자가 현재 처한 상황, 즉 독자가 현재 진실이라고 믿고 있는 바에서 출발해야 한다. 그런 다음 독자를 자신의 논문이 펼쳐지는 세계, 즉 자신의 아이디어가 타당하게 받아들여지는 세계로 안내한다. 마지막으로, 그 세계에서 자신이 기여한 바를 설명한다.
+
+## Each figure must stand on its own
+
+독자가 나의 논문이 읽을 가치가 있는지 빠르게 파악할 수 있도록 하는 최선의 방법은, 논문의 핵심 요점을 설명해 주는 독립적인 그림들을 포함하는 것이다. 예를 들어, 그림 1은 문제를 설명하고, 그림 2는 알고리즘을 설명하며, 그림 3~4는 방법론의 세부 사항을 설명하고, 그림 5~8에는 결과와 분석이 담겨 있을 수 잇다. **중요한 점은 각 그림이 본문을 읽지 않아도 그 자체로도 해석될 수 있어야 한다.**
+
+그림의 내용을 캡션 한 줄로 설명할 수 없다면, 그 그림은 너무 복잡한 것이다. 여러 개의 그림으로 나누어라. 핵심 내용을 한 문장으로 요약할 수 없다면, 역시 너무 복잡한 것이다.
+
+## Write a good conclusion
+
+결론은 초록이나 서론을 과거 시제로 쓴 것이 아니다.
+
+대신, 결론의 목적은 성찰의 시간을 제공하는 것이다. 하지만 이 상기 과정은 간결해야 한다. 가능한 한 빨리 주요 초점으로 돌아가야 한다. 바로 “그래서 뭐?” 라는 질문에 답하는 것이다.
+
+“그래서 뭐?”라는 질문에 대한 답은 “우리 방법이 수치를 2% 상승 시켰다”는 것 이상이어야 한다.
+
+결론이란 ‘네 번째 벽’을 깨고 독자에게 교훈을 직접적으로 전달하는 순간이다. 과감하게 표현해라. 말하지 않은 것이 없도록 하라.
+
+## On Writing
+
+반대로 독자입장으로 본인의 글을 읽어봐라. 어떻게 들리는지 귀를 기울이고, 이해하기 쉽게 만들려고 노력하라는 것이다.
+
+자신이 쓴 글이 독자에게 어떻게 오해의 소지가 있을지 생각해 보라. 특히 두 가지 의미가 있을 수 있는 문장, 그중 하나가 잘못된 해석일 수 있는 문장에는 각별히 주의를 기울여라. 독자로 하여금 한 가지 내용을 말할 것 같다는 인상을 주었다가 정작 다른 내용을 말하는 문장은 피하라. 문장의 문법적 구조가 중요한 단어에 강조가 가도록 하라.
+
+# Conclusion
+
+훌륭한 연구는 (1) 과학적 지식의 체계를 (2) 정확하고 (3) 이해하기 쉽게 발전시킨다. 상을 받게 된다면, 그것은 단지 나의 분포가 어디에 위치했는지 알아차린 것에 불과하다. 분포에 집중하라.
+
+이 때문에, 최우수 논문상을 받는 것을 목표로 연구에 임해서는 안 된다. 이는 건강하지도 않고, 역효과를 낳으며, 우리가 통제할 수 없는 일이다. 하지만 중요하지 않은 논문을 무작정 쌓아두기 위해 연구에 임해서도 안 된다. 그것 또한 마찬가지로 건강하지 않은 일이며, 설령 목표를 달성했다 해도 의미 있는 일을 한 것은 아니다. 대신, 영향력을 미치는 것을 목표로 논문을 작성하라. 그것이 중요한 것이며, 전적으로 자신이 통제할 수 있고, 또한 매우 즐거운 일이다.
\ No newline at end of file
diff --git a/content/writing/human-algorithm-vs-computing-algorithm.md b/content/writing/human-algorithm-vs-computing-algorithm.md
@@ -0,0 +1,91 @@
+---
+title: "휴먼 알고리즘 vs 컴퓨팅 알고리즘"
+date: 2025-10-31
+tags: []
+---
+
+컴퓨팅 알고리즘으로 인해 휴먼 알고리즘이 개입할 기회도 없다.
+
+컴퓨팅 알고리즘은 휴먼 알고리즘과 매우 유사하게 작동하므로 컴퓨팅 알고리즘이 휴먼 알고리즘을 대신하기에는 충분하다.
+
+휴먼 알고리즘은 생각보다 뇌에서 많은 연산과 에너지를 소모하게 만든다. 따라서 에너지 소비를 최대한 줄이고 효율적 방법으로 뇌입장에서는 최대한 휴먼 알고리즘을 사용하지 않는쪽으로 선택하는게 옳바르다. 그러므로 점차 휴먼 알고리즘이 개입할 상황조차 놓이지 않게 되고 점차 우리는 컴퓨팅 알고리즘에 따라 마치 하나의 IoT처럼 큰 정보화 물결에 같이 휩슬리게 된다. 이뿐만이 아니라 지속적으로 휴먼 알고리즘이 작동하지 않아도 되는 상황에 노출이 되면 휴먼 알고리즘을 담당하는 시냅스, 전기적 신호, 신경 등 시스템이 발달하지 못하고 더욱더 컴퓨팅 알고리즘에 의존하게 되는 악순환에 빠지게 된다.
+
+자연적인 휴먼 알고리즘은 뭔가를 배우고 학습하며 지식에 꼬리를 무는 방식으로 생각을 넓혀가고 본인의 주관과 자의식이 결합하여 자신 만의 스토리와 자신만의 세계를 구축하는 것이다. 하지만 지금은 컴퓨팅 알고리즘에 같이 휩슬려 마치 봇처럼 행동하고 사고를 하고 있다. 인간은 완벽한거 같지만 생각보다 쉽게 생각과 행동을 유도할 수 있고(행동 심리학, 인지 심리학 등을 바탕으로) 마치 인간은 본인이 선택한 것 같은 느낌을 받지만 결국 타인이 유도한 대로 움직이고 사고 하게 된다. 이러한 사람이 수백 수천 수만명이 되면 하나의 유행이나 시대적 흐름으로 변질 되고 이러한 물결은 더욱더 주변사람들을 하나의 물결에 휩쓸리게 만들기 쉬워진다. 이렇게 하나의 물결이 만들어지면 하나의 생각 하나의 행동을 강요하고 유도하는게 더욱더 쉬워진다. 인간이란 결국 혼자서는 생존하기 어렵다는 각인된 유전적 사고를 가지고 있기 때문에 매우 특이한 사람을 제외하고 대부분의 인간은 하나의 물결, 하나의 트랜드를 무시하지 못하고 그 물결에 편승하게 된다.
+
+그러면 어떻게 행동해야하고 이런 시대에 어떻게 대응해야할까?
+
+그럼 우리도 똑같이 뇌의 특성을 잘 이용하고 유도해야한다.
+
+인간은 생각을 안하는걸 못한다. 역설적이게도 최고의 효율을 요구하는 뇌는 끊임없이 뭔가를 생각해야한다. 생각을 안하는건 못한다. 이 특성을 잘 이용하자.
+
+이미 세상에는 디지털 티톡스에 대한 방법과 설명이 잘 나와있다. 하지만 갑자기 강제하는건 매우 힘들고 보상을 끊임없이 원하는 뇌 특성상 오래 유지하기 힘들다.
+
+이제 부터 우리는 행동을 하나 강제하면 보상이라는 행동을 하나 더할 것이다.
+
+보상은 생각이나 자신에게 하는 칭찬으로는 절대 안된다. 무조건 눈에 보이는 행동이여야 한다.
+
+예를 들어 인스타를 삭제하면(어플 삭제가 아닌 정말로 계정 자체를 삭제 하는것이다) 평소에 매우 먹고 싶었던 음식 하나를 배달해 먹는것이다. 피자든 스시든 햄버거든.
+
+1차 보상에 성공했으면 50%는 다 온것이다. 그렇게 배달음식을 다 먹고나서 만족이 된 상태로 시간이 지나다 보면 하루든 1주일 뒤든 다시 인스타를 하고 싶을 수도 있다. 근데 막상 인스타를 다시하려고 생각해보니 생각보다 다시하는게 귀찮고 어려울 수 있다. 계정을 하나 만들고 다시 게시물과 릴스를 새롭게 올리며 관심없는 릴스들을 뒤로하고 본인의 관심사에 맞는 게시물이 나올때까지 팔로우와 하트를 누르려면 생각보다 많은 에너지를 요구한다. 물론 그럼에도 불구하고 인스타그램을 다시 하고 싶을 수도 있다. 그럼 다시 배달음식을 시켜 먹어라. 뇌는 포만감이라는 만족감을 얻었으므로 인스타를 하고싶다는 갈증이 없어지거나 줄어들 것이다. 이런 보상 시스템은 뇌를 역이용 하는것이다. 뇌는 보상을 요구하고 충족되면 도파민 같은 호르몬을 보상으로 받는다. 근데 중요한건 충족되는 매체가 무엇이냐는 중요하지 않다. 먹는것이든 인스타를 하는것이든 게임을 하는것이든 뇌 입장에서는 똑같이 포만감을 느끼고 도파민을 보상으로 받을 수 있다. 여기까지는 과정일 뿐이다. 사실 결정적인것은 인스타라는 루틴을 깨는것이다. 이번에는 뇌가 적은 에너지로 최대의 효율을 만들기 위한 시스템인 루틴을 깨는 것이다. 생각보다 우리는 우리가 하고 싶은데로 하는것은 힘들다. 보통은 뇌가 하자는데로 하는 루틴과 같은 무의식이 주를 이룬다. 뇌는 익숙하고 편한걸 좋아하지 뇌의 가용성에 영향을 주는것을 싫어한다. 이런 무의식으로 진행 되는 루틴을 깨야 한다. 인스타도 결국 생각나서 하는경우는 거의 없다. 그냥 하나의 루틴이라서 어느순간 보면 폰을 보고 있고 엄지손가락으로 릴스를 내리고 있는 자신을 인지할 때가 많다. 우리가 지금 까지 한 이 행동들은 결국 인스타그램을 보는 루틴을 깨려는 목적에 기반한 과정이였다. 지금까지 인스타의 릴스를 내리고 보는 과정의 발달된 시냅스와 알고리즘을 서서히 다운그레이드 시키고 다른 보상행동으로 인해 점점 해당 알고리즘에 대한 필요성을 못느끼겠끔 유도하는 것이다.
+
+이건 그냥 나온 행동이 아니다. 내가 직접 경험하고 온몸의 반응을 하나하나 느끼고 기록하면서 만든 일종의 디지털 디톡스이다. 이것을 정확히 이해하기까지 2년이라는 시간이 걸렸다. 온몸의 반응을 정확히 인지하고 느끼는건 처음에는 어렵지만 어느정도 통달하면 그 이후로는 다른 방식의 사고와 행동에도 쉽게 정의가 내려진다. 온몸의 반응을 정확히 인지하는게 어렵지 그 이후에 정의내리거나 원하는 결과를 만들어내도록 유지하는것은 쉽다.
+
+내가 자신의 목소리를 들으라고 강조하는 말이 괜히 한 말이 아니다.
+
+아무튼 이런 디지털 디톡스 뿐만아니라 본인이 하고 싶지 않은, 변화하고 싶은 디톡스, 루틴같은건 이와 같이 만들어내면 된다.
+
+필자는 지방간 극초기라는 진단을 받고 지방간과 체중을 줄이기 위해 탄산음료와 같은 음료를 줄이기 위해 얼음을 대량으로 구매해 갈증이 생길때마다 정말 차가운 얼음물을 마시고 있다. 결국 뇌가 원하는건 갈증해소라는 보상이지 그게 음료수인지 콜라인지는 중요하지 않다. 갈증해소라는 충족만 하면 된다.
+
+이런 과정의 결과는 결국 본인만의 길을 걸을 수 있도록 유도하고 외부 요인에 의해 휩쓸리는 것을 막아주는 체력이 된다.
+
+본인에 대한 행동이나 사고를 완벽히 통제할 수 있는 통제권을 쥐면 외부 요인에 휩쓸일 가능성이 적어지고 저항성도 높아진다.
+
+본인을 완벽하게 통제한 뒤에 여유가 생기면 그 이후부터 주변사람들을 그 물쌀에 휩쓸리지 않도록 작은 보트를 만들어 태울 수도 있고 나중에 큰 배도 만들 수 있다.
+
+그럼 그 배는 큰 물줄기 속에서 이리저리 휩쓸리지 않고 다같이 원하는 곳으로 항해할 수 있는 힘을 가지게 된다.
+
+혼자서 그 큰 물줄기에서 살아남을 수도 있고 버틸 수도 있다. 아니 정말 극악의 확율과 노력으로 어찌저찌 수영해서 원하는곳에 갈 수도 있다.
+
+하지만 본인이 원하고자 하는 방향이 있고 가고싶다면 배를 만들어야지 원하는 곳으로 갈 수 있는 확율이 올라가는건 사실이다.
+
+그러므로 일단 물속에서 버티기위해서 튜브라는 본인만의 휴먼 알고리즘을 만들고 발달시켜야 한다. 컴퓨팅 알고리즘이 아니라 자신이 직접 탐구하고 사고하고 행동하는 주체적인 자아를 만들어 본인만의 알고리즘을 만들고 그 알고즘들을 발달 시켜야 튜브가 구명조끼가 되고 카약이 되고 땟목이 될 수 있다.
+
+다행히 휴먼 알고리즘을 만들고 발달시키는 것은 앞서 말해왔던것에 비해 크게 어렵지 않다.
+
+인류 역사속에서 컴퓨팅 알고리즘에 휘둘리는 역사는 매우매우 짧으므로 이미 뇌와 DNA에 휴먼 알고리즘을 만들고 발달하는 모든 방식을 가지고 있다.
+
+단지 우리가 할 일은 트리거 역할만 하면된다.
+
+앞서 우리가 뇌의 시스템을 이용 했던 것처럼 관심있게 탐구할 거리 하나만 던져주면 된다. 그것이 어떤 단어이든 문장이든 키워드든 상관없다.
+
+우리는 해당 키워드에 대해 아는것과 모르는것을 구부할 것이고 모르는것을 탐구하면서 새로운 지식을 받아들이고 새로운 지식 속에서 또다른 키워드를 발견할 것이고 또 탐구할 것이다. 이 과정이 무한으로 이어지면 다행이도 우리의 수명이 끝나는 날까지 계속해서 탐구할 거리가 끝나지 않을 것이다.
+
+이런 시스템은 인류 역사상 필연적이였고 이런 시스템을 잘 이용하고 잘 활용한 사람이 생존에 유리했고 살아남아 왔다.
+
+옛날만큼 현재는 이런 통찰이 생존에 그다지 영향이 가지는 않지만 지금도 통찰력이 있고 미래를 정확히 예측하는 사람이 보수를 많이 받는건 사실이다.
+
+옛시대 사람들에게는 이런 통찰이나 미래 예측이 생존에 매우매우 중요했다. 농경 사회에서는 기우를 예측했어햐 하고 전쟁에서는 전쟁의 판도를 정세를 정확히 예측 하는 쪽이 생존에 유리했었다.
+
+현시대 글로벌 자유무역 시대 또한, 외교와 경제상황을 정확히 판단하고 예측하는 국가나 기업이 생존에 유리하다.
+
+이렇듯 통찰과 예측은 인류 역사상 생존에 필연적이였고 함께 했다.
+
+통찰이 현대와서야 의미가 많이 줄어든것 같이 보이지만 여전히 큰힘을 발휘하고 있는건 사실이다.
+
+근데 이런 사회야 말로 오히려 역설적이게도 통찰의 힘을 가지고 있는 사람이 더 돋보이고 살아 남을 수 있다.
+
+통찰을 할 수 있는 사람이 점점 적어지고 심지어 지식의 가치도 AI에 의해 줄어드는 판세에 통찰력 마저 없다면 본인의 가치는 줄어들어 결국 기본소득을 받고 자유를 잃고 통제를 당하는쪽이 될 수 밖에 없다.
+
+결국 본인이 정말로 자유를 가지고 행복하려면 우리는 지금 부터라도 본인의 자유의지를 가지고 휴먼 알고리즘을 발달 시키는 것 부터 해야한다.
+
+그것이 현재 뿐만아니라 미래에 자기 자신을 타인으로 부터 통제 되지 않고 완전히 본인이 통제할 수 있고 행복을 누릴 수 있는 첫번째 발걸음이다.
+
+지금 엄지 손가락을 아래에서 위로 올리는 행위는 현재나 미래의 행복에 아무 도움도 안된다.
+
+본인이 정말로 행복이 인생의 목표라면 지금 부터라도 본인의 알고리즘을 만들자.
+
+만약 본인의 목표가 돈, 여자, 남자, 명예라면 아쉽겠지만 위 글은 크게 도움이 되지 못할것이다.
+
+위글은 필자의 행복에 관한 고뇌속에 나온 글이므로 이외의 목표에는 크게 생각해본적이 아쉽게도 없어 도움이 되지 못하는점 양해 바란다.
+
+만약 해당 목표에 대해 정말로 고민해보고 결론이 나온다면 언젠가는 글을 배출하겠다.
diff --git a/content/writing/human-life-from-a-macro-perspective.md b/content/writing/human-life-from-a-macro-perspective.md
@@ -0,0 +1,101 @@
+---
+title: "거시적인 시각으로 보는 사람의 삶"
+date: 2025-10-26
+tags: []
+---
+
+생각보다 삶이 힘들고 지칠때 시각과 마인드를 거시적으로 바라보면 도움이 될 수 있다.
+
+거시적인 입장에서는 해당 문제가 그렇게 민감하게 반응하지 않아도 되고 그다지 중요하지 않을 수 있다.
+
+사실 우리는 평소에 이미 거시적인 시각을 가지고 바라 본적이 많다.
+
+다만 그 순간 순간에는 그러한 시각을 가지기 어렵고 판단하기 어려웠을 뿐이다.
+
+그럼 우리가 대체 언제 우리도 알게 모르게 거시적인 시각으로 바라 봤을까?
+
+바로 과거를 회상했을때다.
+
+과거에 일어났던 당황스럽고 기분이 나쁘고 그당시 불쾌했던 감정을 유발했던 사건들은 지금 생각해보면 막상 별거 아니거나 어쩌면 기억조차 나지 않거나 ‘그당시 왜 그렇게 민감하게 반응했을까?’ 하고 후회를 할 수도 있다.
+
+이렇든 우리 모두는 거시적인 시각을 이미 가지고 있다.
+
+단지 우리가 어떤 사건에 의해 즉각적으로 반응할때는 그런 시각을 가지고 보지 못할 뿐이다.
+
+그렇담 답은 이미 간단하다. 어떤 사건이 지금 당장 닥쳤을때 이미 가지고 있는 거시적인 시각으로 바라보는것이다.
+
+우리는 타인에 의해 쉽게 기분이 나빠지는 순간들이 많다.
+
+출근하고 있는 지하철에서 누군가 신발을 밟거나 갑자기 자리를 빼앗거나 가방으로 밀치는 등 많은 불쾌한 상황에 쉽게 노출되어 있다.
+
+그럼 우리는 그 불쾌한 상황을 직면하고 불쾌감을 느끼며 어떨때는 불쾌감을 표출하고 불쾌한 감정을 가지고 있다가 타인에게 토로 한다.
+
+그 불쾌한 감정은 우리가 다른 불쾌한 일로 잊을 때까지 계속해서 가지고 있고 상기 시켜 우리의 하루를 종일 기분이 나쁜 상태로 있게한다.
+
+이 얼마나 슬프고 단순한가.
+
+타인으로 인해 본인의 삶이 하루종일 기분이 좋지 않은 상태로 만들다니.
+
+심지어 긍정적인 감정도 아닌 그 불쾌감을 타인에게 배설을 하고 전파한다.
+
+타인을 통해 자신의 감정이 통제되는 상황을 내버려 두지 마라.
+
+왜 다른 사람으로 인해 본인의 감정이 하루종일 네거티브한 상황으로 만들어 두는가?
+
+본인의 감정은 본인이 통제하고 지키자.
+
+이럴때 거시적인 시각이 도움이 된다.
+
+구지 이해하려 노력하지 않아도 그냥 “신기한 사람이네” 하고 생각하면 크게 기분이 나쁠이유도 없다.
+
+이 “신기한 사람이네”라는 문장은 내가 그동안 여러 상황에 노출되면서 고안해낸 문장인데 생각보다 가성비가 매우 좋은 문장이다.
+
+실제로 그런 불쾌감을 주는 사람은 뇌 입장에서도 정말로 “신기한 사람” 이기때문에 내가 느끼는 감정을 억지로 거짓으로 바꾸거나 속이지 않을 필요도 없다.
+
+그래서 자연스럽게 뇌 입장에서도 “신기한 사람, 신기한 상황"으로 인지하고 넘어가 버린다.
+
+어쩌다 보면 실소까지 나오게 될정도로 긍정적이고 중의적인 표현이다.
+
+특히 종교를 가지신 분들이 이런 거시적인 시각을 잘 활용하신다.
+
+훈련이 아니라 진정한 믿음을 가지신 분들은 그저 불쾌한 상황에 노출됐을 때 그저 ‘하나님의 뜻, 부처님의 뜻' 등 각자의 신의 입장에서 생각하지 인간의 입장에서 생각하려 하지 않기 때문에 가능한 결과라고 생각한다.
+
+사실 종교를 가지신 분의 거시적인 시각과 내가 말하는 거시적인 시각은 목적이 다르지만. 어쨌든 결과는 비슷하다.
+
+종교를 가지신 분은 단순히 한낱 인간의 사고로는 신의 뜻을 헤아릴 수 없으니 인간이 판단하는건 무의미하다는 의미로 바라보는 것이다.
+
+그래서 기분 나쁜 상황이 오거나 큰 사고가 있더라도 감정을 크게 동요하지 않고 신의 뜻이라는 거시적 목적아래 계속 나아가는 것이다.
+
+즉, 거시적인 시각은 종교적 거시적 시각이든 일반적인 거시적 시각이든 우리가 삶을 살아가는데 있어서 큰 목적을 잊지 않도록 도움을 준다.
+
+우리의 본질, 삶의 목표는 어디까지나 행복한 삶이기 때문이다.
+
+근데 안타깝게도 거시적인 시각으로 보려고 노력하는 삶은 그동안 미시적인 시각으로 살아온 사람이 노력으로 거시적인 시각으로 살아가는것이 변화가 가능하듯이
+
+반대로 거시적인 시각을 가지고 살아가던 사람도 환경에 의해 미시적인 시각을 가진 삶으로 변모 하는 과정도 많이 지켜보았다.
+
+실제 사례로 이런 시각을 이미 가지고 있던 시절에 편의점 아르바이트를 한적이 있었다.
+
+점장은 대기업 보험회사에서 손해사정사로 10년 이상 재직하다 퇴사하고 편의점을 이제막 차린 사람이였다.
+
+아직 편의점이 완공되기 전 면접때부터 긍정적이고 좋은 인상을 풍겨 흔쾌히 면접을 진행하였고 그렇게 첫 직원이 되었다.
+
+편의점 알바 경력이 있었던 나는 편의점 시스템을 잘몰랐던 점장을 대신해 공산품목 진열과 인기품목을 분석하고 발주하였다.
+
+따로 시킨것은 아니였지만 도움을 준다는게 즐거웠고 심지어 즉각적인 매출 증가나 기존 동네 사람들의 칭찬과 고마움 표시로 인해 더욱더 열심히 했다.
+
+운이 좋게도 점장은 내 짐심을 알아줘서 시급을 더욱더 올려주었고 평범한 직장인 부럽지 않게 월급을 받을 수 있었고 복지도 받을 수 있었다.
+
+또한 기존 편의점 시스템에 없었던 지금으로 치면 ‘나만의 냉장고’를 영수증을 이용한 수동 서비스를 만들어 더욱더 매출이 증가 할 수 있었다.
+
+점장이나 나나 지금 당장 이익이 안되고 손해더라도 지속적인 단골을 만들기 위해 서비스를 개선하고 만드는것에 공통적인 생각을 가지고 있어 즐겁게 일할 수 있었다.
+
+하지만 건물주가 월세를 인상하려 하고 코로나로 인해 수익악화가 나면서 점장의 여유가 사라진것 같았다.
+
+물론 어디까지나 나는 직원일 뿐이였기에 점장의 마음을 100% 다 이해할 수는 없지만 그래도 거시적인 시각을 잃게 되는게 안타까웠다.
+
+이외에도 다른 사람들과 상황을 관찰하면서 느꼈지만 거시적인 시각을 가지고 있는 사람도 환경에 따라 미시적으로 변화하는 과정을 생생하게 관찰한적은 처음이였다.
+
+이렇듯 환경에 따라 시각은 얼마든지 변할 수 있기때문에 거시적인 시각을 가지고 유지하고자 목표로 한다면 생각보다 처음에는 가지는게 쉬울지 몰라도 유지하는건 힘들 수 있다는걸 말하고 싶다.
+
+따라서 거시적인 시각을 유지하기 위해서는 우리의 마음의 그릇을 최대한 넓게 만들고 훈련을 통해 채워나가야 한다. 그래야 어떤 상황에 처해지더라도 그릇이 비워지는데 시간이 걸리고 심적 여유, 체력이 없다면 더욱더 그릇이 빠르게 빌 것이기 때문이다.
diff --git a/content/writing/i-have-to-i-have-to-i-have-to.md b/content/writing/i-have-to-i-have-to-i-have-to.md
@@ -0,0 +1,143 @@
+---
+title: "해야한다 해야한다 해야한다"
+date: 2025-10-19
+tags: []
+---
+
+할게 너무 많다. 너무 많아서 선택을 해야할 상황까지 와버렸다.
+
+선택을 하면 또 선택이 기다리고 선택의 연속이다. 선택에는 끝이 없다.
+
+우리는 태어나서 부터 ‘~하는것’ 부터 배웠다.
+
+말하는법, 감정표현 하는법, 인사하는법, 공부하는법, 그림그리는법 등등
+
+평생 무언가를 해야한다.
+
+안다, 사실 인간이라는 동물은 끊임없이 뭔가를 해야한다.
+
+그렇게 설계가 된 유기체이다.
+
+인간이라는 유기체는 계속해서 외부에서 자극이 일어나야 본인의 존재를 인지할 수 있다.
+
+자극이 없으면 자극을 만들어서라도 본인의 존재를 인지하는게 인간이다.
+
+이 얼마나 비효율적인가.
+
+인간은 가만히 있는 불빛도 스스로 동공을 움직여서 불빛의 존재를 확인한다.
+
+신체 자체가 가만히 있지 못하도록 설계가 되어있다.
+
+그래서 우리는 끊임없이 무언가를 해야하고 해야한다는 강박에 쌓인다.
+
+1초라도 쉬려고 하면 거부반응이 온다.
+
+이런 비효율적인 시스템이 결국 우리 인류 문명을 빠르게 발전시켰다는 사실은 부정하지 않겠다.
+
+인간이란 결국 죽을때까지 계속 생산을 하도록 설계된 동물이니까.
+
+하지만 현재는 상황이 다르다. 구지 생산적인 것을 하지 않더라도 외부의 자극에 쉽게 노출되어 있다.
+
+엄지손가락을 밑에서 위로 올리는 아주 간단한 동작 하나만으로 새로운 자극을 계속해서 얻을 수 있다.
+
+가성비가 너무 좋다.
+
+단순히 엄지손가락을 “까딱” 하는것 만으로도 방대한 량의 정보의 자극이 뇌로 들어온다.
+
+이 말도안되는 극강의 에너지 효율성은 하루종일 해도 질리지가 않고 체력적인 문제가 발생할 일도 없다.
+
+그 “까딱”하는 엄청난 가성비 행동으로 얻은 정보는 뭔갈 해야한다는 심리적 작용으로 연결된다.
+
+나도 여행가고 싶다. 저 카페 가고 싶다. 저거 먹어보고 싶다. 나도 보고싶다. 연애하고 싶다. 저렇게 멋진 사람이 되고싶다. 재밌다.
+
+정말 재밌는게 맞을까? 정말 그걸 보면서 행복감이 충족이되는걸까?
+
+난 절대 아니라고 본다.
+
+보는것만으로도 행복이 충족이 되었다면 왜 계속해서 보고 있는가?
+
+부족하고 결핍이되어 있으니까 계속해서 보는거 아니겠는가?
+
+행복은 커녕 오히려 뭔가를 하고싶다는 강박만 쌓이게된다.
+
+해야하는건 끝도 없이 많다. 아니 죽을때까지 절대 다 못한다. 돈이 아무리 많아도 죽을때까지 다 하고 못죽는다.
+
+무조건 지는 싸움이다.
+
+우리는 왜 뭔가를 해야한다는 강박에 쌓인걸까.
+
+사실 하지않아도 크게 생존에 문제가 되거나 사회적으로 결려가 되거나 그렇지 않다.
+
+실제로 나는 카카오톡과 모든 SNS를 탈퇴하였지만 외부적으로 아무런 변화가 찾아오지 않았다.
+
+나는 반대로 생각해보기로 했다.
+
+우리는 뭔가를 하고 하는건 배우는데 하지않고 하지말아야 하는건 왜 안배우고 소홀히할까?
+
+음악을 듣지않는 법, 침묵을 유지하는 법, SNS를 보지 않는법, 자극적인 포르노를 보는법.
+
+생각해보면 우리는 누가 가르치지 않아도 스스로 SNS를 시작하고 음악을 들었으며 자극적인 포르노를 시청하고 손톱을 물어 뜯기도 한다.
+
+신기하지 않은가? 인간은 외부 자극을 통해 지속적으로 본인의 존재를 인지하겠금 설계되었다는 사실이 우리가 따로 학습하지 않아도 스스로 외부자극을 찾고 하도록 설계되었다는것이 매우 놀랍다.
+
+우리는 본인을 통제할 수 있다고 믿지만 사실 우리는 외부 자극이 없으면 불편함을 느끼는 존재이다.
+
+좋다. 자연의 섭리를 거스를 수는 없으니 자극을 차단하고 신경계를 잘라버리는건 말도 안된다.
+
+근데 다시한번 곰곰히 생각해보자. 우리가 과연 “외부에서 오는 자극”이 없으면 우리는 우리의 존재를 느끼지 못할까? 그렇다면 우리 선조는 그동안 어떻게 생존해 왔으며 어떻게 본인의 존재를 인지할 수 있었을까?
+
+사실, 답은 아주 간단한 실험을 통해 얻을 수 있다.
+
+지금 당장 5분동안 눈을 감고 이어플러그가 있으면 이어플러그를 끼고 소리가 없는 환경에서 가만히 있어봐라.
+
+본인 온몸의 신경 자극이 느껴지고 뇌가 말하는 이야기가 들릴것이다.
+
+100% 뇌는 이렇게 소리치고 있을 것이다. “눈을 떠! 귀를 열어! 답답해!!”
+
+축하한다. 방금 당신은 처음으로 뇌가 말하는 내면의 소리를 들었다.
+
+어떤가, 외부 자극이 없으니 아무도 듣지 못하는 본인 내면의 소리를 들을 수 있지 않았나?
+
+사실 우리의 내면의 소리는 그동안 줄기차게 소리를 질렀다.
+
+듣지 못하고 들으려 하지 않은건 본인이다.
+
+이어폰을 끼고 노래를 들으며 출퇴근을 하고 핸드폰을 보며 사람들과 소통하고 자극적인 미디어를 보며 자는시간을 제외하고 계속해서 뭔가를 듣고 뭔가를 본다.
+
+그렇게 내면의 소리를 들을 시간은 외부 자극에 의해 묻히고 만다.
+
+수만은 센서를 통해 외부 자극을 읽어 들이는데 내면의 소리를 들을 시간이 어디 있겠는가.
+
+우리 이제 내면의 소리를 듣자.
+
+내면의 소리도 내면의 자극 중 하나라 우리의 존재를 인지하는데에는 문제가 없다.
+
+사실 외부적 자극이든 내부적 자극이든 우리 뇌는 자극만 있으면 본인의 존재를 인지하는데에는 문제가 없기 때문이다.
+
+그냥 그동안 우리는 외부적 자극 요인에 자극을 빼앗긴 것이고 그게 익숙하고 편했기 때문이다.
+
+그래서 사실 그동안 우리가 무언가를 열심히 한건 뇌 입장에는 크게 의미가 없다.
+
+없어도 뇌에서 받아들이는 자극에는 아무 문제가 없기 때문이다.
+
+이제 우리는 앞으로 하지 않고 하지 말아야될 이유와 명분을 확고히 정립했다.
+
+SNS 안해도 된다. 음악 안들어도 된다. 자극적인 영상 안봐도 된다.
+
+앞으로 그 시간을 내면의 소리를 듣는 시간으로 치환해라. 내면이 그동안 하고싶었던 말을 귀담아 듣고 실천하고 행동해라.
+
+“~하지말라면서요. 안해도 된다면서요”
+
+외부의 소리와 달리 내면의 소리는 본인의 생존을 위해 진심어린 말을 내뱉는다.
+
+내면 본인의 진정한 행복을 위해 진심어린 말을 본인에게 전하기 때문이다.
+
+그게 진정한 행복을 추구하기 위한 가장 빠르고 간단한 해결책이다. 답은 가까이에 있었다. 아니 본인에게 있었다.
+
+앞으로는 “~하기” 보다 “~하지 않기”도 실천해보고 연습해보기 바란다.
+
+죽지 않는다. 아니 진정한 행복을 위해서는 꼭 선행해야할 연습이다.
+
+만약 본인의 진정한 행복을 추구하는 삶을 원한다면 내면의 소리를 듣기 바란다.
+
+아무도 듣지 못하는 내면의 소리이기 때문에 부끄러워할 필요도 없다.
diff --git a/content/writing/journalctl.md b/content/writing/journalctl.md
@@ -0,0 +1,109 @@
+---
+title: "journalctl"
+date: 2025-12-07
+tags: []
+---
+
+# journalctl
+
+- 리눅스 시스템의 systemd 가 관리하는 모든 로그를 조회하고 검새하는 도구
+- 과거에는 /var/log/syslog, /var/log/auth.log 등 테스트 파일을 일일이 열어봐야 했지만, 이제는 journalctl 명령어 하나로 통합 관리
+
+## **journalctl -e** VS **journalctl -b -1 -e**
+
+두 명령어의 가장 큰 차이는 로그를 보는 시점(Timeframe) 이다.
+
+| 명령어 | journalctl -e | journalctl -b -1 -e |
+| --- | --- | --- |
+| 의미 | 현재 부팅(current Boot) 로그의 끝부분 | 직전 부팅(Last Boot)로그의 끝부분 |
+| 탐색 범위 | 부팅된 순간(0) -> 현재 시점 | 직전 부팅 시작 ~ 직전 부팅 종료(Crash) |
+| 사용 예 | 서비스 재시작 후 에러 확인, 실시간 모니터링 | 커널 패닉, 정전, 하드웨어 오류로 인한 비정산 재부팅 원인 |
+
+### 상세 분석
+
+- journalctl -e
+ - (Pager End): 스크롤을 맨 아래(최신)로 내려서 보여준다.
+ - 기본적으로 현재 부팅된 세션의 로그를 보여준다.
+- journalctl -b -1 -e
+ - -b -1 (Boot offset -1): 현재(0) 말고 바로 전(-1) 부팅 기록
+
+### journalctl 필수 옵션
+
+#### 실시간 감시 (-f)
+
+새로운 로그가 들어오면 계속 화면에 출력한다.
+
+```bash
+$ journalctl -f
+```
+
+#### 특정 서비스만 보기 (-u)
+
+특정 서비스(Ex. ssh, tailscale)만 선택하여 확인한다.
+
+```bash
+$ journalctl -u ssh
+```
+
+#### 커널 메시지만 보기 (-k)
+
+하드웨어 오류(랜카드, 디스크), 방화벽 차단 로그 등은 주로 커널(Kernel) 이 남긴다.
+
+```bash
+$ journalctl -k
+```
+
+#### 시간 범위 지정 (--since, --until)
+
+특정 시간에 발생한 사건을 조회
+
+```bash
+$ journalctl --since "2024-12-07 14:00:00" --until "2024-12-07 15:00:00"
+$ journalctl --since "1 hour ago" # 1시간 전부터 지금까지
+```
+
+#### 에러만 보기 (-P)
+
+정보(Info) 로그는 무시하고, 오류(Error)나 경고(Warning) 등 심각한 것만 확인
+
+```bash
+$ journalctl -p err # 에러(3) 등급 이상만 표시
+```
+
+### 로그 영구 저장 설정(비휘발성)
+
+#### 설정 파일 열기
+
+```bash
+$ sudo vim /etc/systemd/journald.conf
+```
+
+#### 옵션 수정
+
+```vi
+[Journal]
+Storage=persistent
+SystemMaxUse=500M
+```
+
+- Storage=persistent : 하드디스크에 영구 저장
+- SystemMaxUse=500M : 로그 파일이 너무 쌓여서 디스크를 꽉 채우지 않게 최대 500MB까지만 저장
+
+#### 서비스 재시작
+
+```bash
+$ systemctl restart systemd-journald
+```
+
+#### 적용 확인
+
+```bash
+$ ls -d /var/log/journal
+/var/log/journal
+```
+
+### References
+
+1. https://www.freedesktop.org/software/systemd/man/latest/journalctl.html
+2. https://manpages.ubuntu.com/manpages/noble/en/man1/journalctl.1.html
+3. https://wiki.archlinux.org/title/Systemd/Journal
diff --git a/content/writing/must-do-must-buy-must-try-must-see.md b/content/writing/must-do-must-buy-must-try-must-see.md
@@ -0,0 +1,29 @@
+---
+title: "꼭 해야한다 꼭 사야한다 꼭 먹어봐야 한다 꼭 봐야 한다"
+date: 2025-10-17
+tags: []
+---
+
+꼭 이란건 없다. 그냥 마음대로 가져다 붙이는 단어이다.
+
+‘꼭’ 이라는 단어는 이상하다. 단순히 1글자인데도 그 영향력은 어마무시하다.
+
+‘꼭’ 이라는 단어를 본 순간 진짜 해야할것 같다. 먹어봐야 할거 같다. 봐야할거 같다. 그렇지 않으면 한번뿐인 인생에서 손해보게 되는것 같다.
+
+아니?
+
+잘 보면 ‘꼭’ 이라는 단어는 인생에서 꽤 많이 보이고 꽤 많이 듣게 된다.
+
+그만큼 ‘꼭’이라는 단어의 희소성은 희미해진지 오래다.
+
+그냥 마케팅 용어일 뿐이다.
+
+‘꼭’이라는 1단어는 정말 가성비가 좋다.
+
+‘꼭’ 이라는 단어만 앞에 붙어도 많은 내용을 내포하고 있다.
+
+진짜 사야 하나? 안하면 불이득인가? 단어 자체에 강제성은 없지만 강제성을 유발하고 내포한다.
+
+우리 인생에서 ‘꼭’ 이란건 없다. 꼭 해야할것도 없고 꼭 사야할것도 없다. 꼭 먹어봐야할것도 없다.
+
+“꼭”이라는 단어에 휘둘리지 마라.
diff --git a/content/writing/on-capturing-and-posting.md b/content/writing/on-capturing-and-posting.md
@@ -0,0 +1,81 @@
+---
+title: "찍고 올리는 행위에 대하여"
+date: 2025-11-21
+tags: []
+---
+
+오늘 퇴근길에 유난히 사람이 많아 지하철 승강장 계단에서 부터 사람들로 인해 막혀 움직이지 못하는 상황에 처하게 되었었는데 주변에 있는 사람들이 마치 약속이라도 한것 마냥 스마트폰을 높이 들고 현재의 상황을 찍고 인스타 스토리에 올리는 행위를 하는 것을 보고 1 1차적으로 신기하다는 감정이 들었고 2차적으로 왜 저런 행위를 하는걸 까? 하고 무수한 궁금증이 유발되었고 퇴근길 내내 집에 와서도 곰곰히 생각해보고 몇가지로 정리해 볼 수 있었다.
+
+먼저 본인의 현재상황을 찍고 모두가 볼 수 있는 공개된 SNS에 올리는 것 자체가 “인증”이라는 범주에 들어가는 지 “증명”이라는 범주에 들어가는 지 궁금하였다.
+
+일단 인증이라는 행위에 더 가깝다는 결론에 이르렀다.
+
+SNS라는 서비스 자체가 사실을 증명하는 것보다 타인에게 공감을 받고 기록하는 인증이라는 행위에 가깝다고 생각한다.
+
+물론 그 인증을 통해서 사실을 증명 할 수 있지만 원론적으로는 공감을 얻고 기록하는 인증에 더 가깝다 생각한다.
+
+그렇다면 사람들은 어째서 이런 소위 “인증샷”같은 행동을 하면서 까지 불특정 다수에게 공유하는 행동을 하는것일까?
+
+인간의 본질에 최대한 입각해서 생각해 보았다.
+
+특정 그룹만 그런것이 아닌 보편적인 사람들 평균 즉, 최대한 본능에 가까운 행동을 대입하며 생각해 보았다.
+
+첫번째로 존재감 확인이다.
+
+지금 열심히 사진을 찍어 대는 사람들은 평소의 출퇴근을 찍는 사람들이 아니다.
+
+사람이 너무 많아 계단도 못올라 갈정도의 특수한 상황이기 때문에 찍고 공유하는 것이다.
+
+말이 순서가 바뀌었다. 찍는건 수단이고 목적은 공유이기때문에 이렇게 정정하겠다.
+
+사람이 너무 많아 계단도 못올라 갈정도의 특수한 상황이기 때문에 공유하기 위해 찍는 것이다.
+
+이런 특수한 상황은 모두에게나 일어나는 것이 아니다.
+
+말 그대로 평소와 다른 특수한 상황이기 때문에 언제 이런 일이 일어날지 모르니 현재 본인이 이렇게 가능성이 희박한 특수한 상황에 놓여져 있다는 것에 대해 공감받기 위해, 기록하기 위해 공유하는 것이다.
+
+두번째로 방어 기제 본능에 입각한 고통을 콘텐츠로 소비하면서 거리를 두는 심리이다.
+
+금요일 꽉 막힌 퇴근길 누가 좋아할까? 다들 집에 빨리 가고 싶고 쉬고 싶은 사람들일 것이다.
+
+꽉막힌 퇴근길을 좋아할 사람도 없고 그런 상황이면 스트레스만 받을 것이다.
+
+하지만 그러한 스트레스를 자기 희화화를 통해 어느 정도 줄일 수 있다면?
+
+자기 희화화는 인간의 스트레스 방어 기제 본능 중 하나이다.
+
+본인을 희화화 하면서 자신이 움직이지도 못하고 꽉 끼어 있는 피해자로서만 존재한다는 것을 망각하게 하고 이 황당한 상황을 촬영하고 공유하는 관찰자 입장을 취하게 만들어 스트레스를 덜어준다.
+
+“이것봐, 진짜 미친 거 아니야? ㅋㅋㅋ” 라며 상황을 희화화함으로써, 스트레스를 웃음이나 콘텐츠로 승화시켜 심리적 고통을 줄이려는 인간의 무의식적인 방어 기제이다.
+
+세번째로 인정 욕구이다.
+
+신기하게도 사람들은 화려한 인생, 화려한 삶 뿐만 아니라 본인의 고생, 노고도 공유하고 알아봐주길 원한다.
+
+“나 이렇게 고생한다”, “나 이런 삶을 살아간다” 라는 사진을 공유하면서 사람들의 위로 댓글을 유도하면서 그 위로 댓글로 스트레스 받는 현재 상황을 보상 심리로 채우는 무언의 호소이다.
+
+네번째로는 집단 소속감 심리를 이용한 공감의 연대이다.
+
+우리는 소속감을 계속 넓히고 싶어 한다.
+
+화려한 사진, 화려한 음식들은 호불호가 갈리거나 질투를 유발할 수 있지만 지옥철 같은 사진은 다른 일반 직장인들의 공감대도 형성하고 소속감을 더욱더 넓힐 수 있다. 나도 평범한 직장인 이라는 인상을 심어주어 더 넓은 공감대를 형성하고 불특정 다수에게 더 넓은 소속감을 느끼고 느끼게 할 수 있다.
+
+물론 또 다른 심리가 작용하여 “인증샷”이라는 행위를 하는 사람도 있겠지만 지금 생각할 수 있고 정의 내릴 수 있는건 이정도 범주 내에 거의 들어 온다 생각한다.
+
+결국 이 4가지 심리적 요인 모두 인간의 생존에 영향을 받는 심리라 더욱더 크게 작용하고 이성의 개입없이 무의식적으로 작동하는 행동으로 보인다.
+
+그렇다면 여기에서 생긴 이어지는 궁금증.
+
+나에게 있어 블로그에 글쓰는 건 무슨 행위일까?
+
+masturbation 즉 자위이다.
+
+필자의 글을 본 사람들은 알겠지만 나는 블로그에 글을 올릴때 배출한다고 표현한다.
+
+말 그대로이다.
+
+마치 자위로 성욕을 배출하듯이 나는 복잡하고 꽉막혀있는 생각이라는 덩어리를 배출하고자 하는 욕망을 글로써 배출하는 것 뿐이다.
+
+그래서 나의 글은 욕망의 산출물이다.
+
+여러분은 내 욕망의 산출물들을 보고 있는 것이다.
diff --git a/content/writing/rambling-thoughts-about-money.md b/content/writing/rambling-thoughts-about-money.md
@@ -0,0 +1,101 @@
+---
+title: "돈에 관하여 생각하다 횡설수설"
+date: 2025-10-26
+tags: []
+---
+
+내 삶의 목표는 언제나 어딘가에 종속되어 있는 것이 아닌 내가 내삶을 주체적으로 살아가고 자유롭게 선택하며 유동적으로 살아가는 삶을 목표로 하고 있다.
+
+그래서 그동안 돈이라는 ‘것’에 대해서는 크게 의미를 부여하지 않았고 깊이 생각해 본적도 딱히 없었다.
+
+나에게 있어서 돈이라는 ‘것’은 있어도 그만 없어도 그만인 ‘것’이였기 때문이다.
+
+(딱히 어떻게 표현해야 할지 몰라 ‘것’이라 표현하겠다)
+
+자유롭고 행복한 삶에 돈이라는 건 비중이 낮다 생각했다.
+
+그도 그럴 것이 최소한의 돈만 있으면 인간이 살아가는 데 큰 문제가 없었고 돈이란 그져 인간의 욕심과 허영심 때문에 돈이 계속 부족하다 느끼며 돈에 얽매이는 삶이 시작된다고 느껴 돈을 최대한 경계하였다.
+
+근데 이렇게 말하는걸 보면 내가 그동안 돈을 부정적으로 생각하고 편견을 가지고 있었단걸 알 수 있다.
+
+돈을 도구로 바라보고 돈을 수단으로 바라보면 되는 것을 나는 돈의 결과론적인 나쁜 면만 보고 판단하고 있었다.
+
+그러다 보니 돈에 대해 무지했었고 깊이 생각해 본적도 딱히 없는것 같다.
+
+몇일간 돈에 대해 생각해 봤다.
+
+돈이 가져오는 긍적적인 방향과 돈의 장점에 대해 생각해 보았다.
+
+돈은 무겁다.
+
+돈은 주체적으로 삶을 살아가고 자유롭게 선택하는데 도움이된다.
+
+여기서 중요한건 “도움이 된다” 이다.
+
+돈은 확실히 내 삶을 주체적으로 살아가는데 도움이된다.
+
+하지만 그만큼 선택을 하는데 돈의 개입이 언제든 들어올 수 있고 그 선택에 돈이 크게 잠식할 수 있다.
+
+물론 못하는 것과 안하는 것은 다르다.
+
+돈이 없으면 나에게 선택지는 하지 못한다는 선택지가 하나이지만
+
+돈이 있으면 할 수도 있고 안할 수도 있다. 선택지가 두개가 된다.
+
+단, 이 선택 과정에서 돈의 개입이 정신줄을 놓으면 크게 작용한다는 무서운 점이 있기 때문이다.
+
+만약 뭔가를 사거나 뭔가를 하는데 돈이 충분히 있게 된다면 우리는 하는 쪽에 무게를 둘 가능성이 매우 높다.
+
+뇌 시스템 자체가 끊임없는 보상을 바라기도 하고 본능상 당장의 이익을 얻는것 선호하기 때문이다.
+
+돈이 충분히 있는데 안할 이유도 사라지고 하지 않을 이유마저도 여러가지의 인지부조화 후의 자기합리화 과정을 거치면서 흐려지기 때문이다.
+
+본능을 거스르는건 매우 힘들다. 편도체에 조금이라도 기회를 주면 실행 시켜 버리기 때문이다.
+
+일단 실행 시키면 후회하는건 다른 뇌이기 때문에 지금 당장의 보상을 원하는 편도체 입장에서는 안 할 이유가 없다.
+
+그래서 돈이 있으므로서 선택할 수 있는 기회가 많다는건 사실이지만 그만큼 현명한 선택을 하지 못하는 사람에게는 오히려 독이 될 수도 있다.
+
+이런 사람에게는 차라리 돈이 부족함으로서 못하는게 선택에서 오는 스트레스를 줄일 수 있다.
+
+하지만 현명한 선택을 할 수있고 돈이란건 도구에 불과하다는걸 잘 인지하고 있는 사람이라면 오히려 선택의 과정에서 자유를 줄 수 있고 더 많은 가능성을 염두할 수 있다.
+
+돈은 도구일 뿐이라 완벽히 통제한다는건 불가하기 때문에 큰 돈이 쥐어 졌을때 본인이 통제가 가능한 사람인지 부터 파악하고 돈을 저축하는게 먼저라고 생각한다.
+
+본인 부터 통제가 불가능한 사람이라면 아무리 돈이 많아 봐야 의미가 없다. 선택으로 인한 자유도 누리지 못하고 고민과 후회만 반복하는 스트레스만 있는 삶을 살아갈뿐이다.
+
+나는 미니멀리즘의 삶을 지향하며 살아가고 있다.
+
+미니멀리즘을 3년째 하면서 느낀거지만 도구는 많을 수록 스트레스를 준다는 것이다. 우리가 가지고 있는 여러 도구들을 계속해서 지키고 하루하루 지켜내기 위해서 생각 보다 많은 신경을 쓰고 체력을 고갈 시킨다는 점이다.
+
+결국 부족한 체력은 본질을 보는 눈을 흐리게 만들고 탁하게 만들어 우리의 삶을 앞으로 전진하지 못하게 하고 이리저리 방황하고 길을 찾는데 시간을 허비하게 만든다.
+
+본질을 볼 수 있는 눈을 가지면 길이 명확하게 보이고 하나의 길만 보여 우리가 길을 해매는데 시간을 허비하지 않게 해준다.
+
+그런 이유에서 미니멀리즘의 삶은 흐려진 본질을 보는 눈을 닦아준다. 단순히 물건을 버리고 비우는 것이 아니라 비움으로서 본인에 더욱더 집중하고 여유 있는 삶을 가질 수 있도록 도와줄 뿐이다.
+
+미니멀리즘의 본질은 마음을 비우고 우리 삶의 본질을 찾는데 중점을 두기 때문이다. 사실 말이 미니멀즘이지 불교적, 기독교적 아니 다른 종교적 가치관, 철학과 크게 다른건 없다. 불교 가치관으로 표현하자면 ‘무소유’고 기독교 가치관으로 표현하면 ‘청빈’ 이다.
+
+이런 미니멀리즘 가치관은 갑자기 나온게 아니라 인류역사의 시작부터 우리와 함께 해왔다는건 확실하다. 근데도 불구하고 우리 인간은 아직까지도 본질을 추구하는 삶을 살지 못하고 있고 추구하고 있다.
+
+본질을 추구하는 삶을 다들 원하지만 쉽게 하지 못하는 이유는 단순하다.
+
+유혹도 많고 신경쓸 것도 많고 특히 재미없기 때문이다.
+
+심지어 본인이 지금 선택하는게 본질에 위배가 된다는걸 알면서도 잘못된 길을 선택하는게 인간이다.
+
+그렇기에 본질을 추구하는 삶을 유지하는 건 어려운 일이고 존경 받을 만한 일이다.
+
+그렇기에 본질을 보는 눈을 가진 사람은 귀하고 찾기 어렵다.
+
+또한 본질을 추구하는 삶을 살아온 사람에게 보상은 확실하다. 차별점이 명확하기 때문이다. 특히 요즘같이 본질을 보는 눈을 가지기 힘들고 그런 사람을 찾기 힘든 세상속에서는 더욱더.
+
+결국 돈도 도구이다. 하나의 도구가 더 추가된거다. 도구가 추가된 만큼 신경쓸 거리가 하나 늘었다. 우리는 추가된 도구에 신경을 쓸 수 밖에 없다. 추가된 도구에 신경을 쓸 수 있을 만큼 본인이 여유가 있고 체력이 있는가? 그럼 돈을 모아도 된다. 하지만 본인이 그렇지 못한 사람이라면 그 도구는 오히려 본인의 삶에 독이 될 뿐이다.
+
+우리는 주체적인 삶에 행복감을 느낀다. 주체적인 삶이란 본인에게 선택권이 있는 삶이다. 하지만 어느 순간 부터 우리는 편리함이라는 도구 앞에 우리의 자유와 선택권을 자진 반납하고 있다.
+
+수많은 서비스들이 편리함이라는 가면 뒤에 우리의 자유와 선택권을 요구하고 있다. 그 서비스들이 우리의 개인정보와 데이터를 읽을 권리는 없다. 하지만 우리는 무료로 또는 몇십원 몇백원이라는 소득을 위해 스스로의 권리를 싼값에 판매하고 있다. 심지어 그게 당연하고 현명한 수입을 얻는 것처럼 앱테크라는 멋진 신조어도 생겼다.
+
+나는 너무 무섭다. 이제 사람들은 어떤 독제자에 의해 자신의 권리를 강제로 빼앗기는게 아닌 자발적인 참여로 본인의 데이터와 권리를 내어주고 있다. 어떤 결과로 이어질지는 아직 모르겠지만 이런 과정을 지켜보는, 이런 시대적 물결속에 흘러가는 한 사람으로서 두려운건 사실이다. 심지어 본인이 선택했으니 주체적인 삶에 포함되 몇백원의 수입에 행복감을 느꼈을 수도 있다.
+
+물론 선택에 따른 책임은 본인이 지는건 당연하겠지만 이런 행태가 점점 당연하고 현명한 시각으로 변모하는 것 같아서 두렵다. 수천년 인간의 본질 특성상 행복하지 않게 될 것이라는 결론이 눈에 보이기 때문이다. 이런 과정이 끊이지 않고 지속된다면 긍정적인 결말의 이야기가 아니라는건 확실하다.
diff --git a/content/writing/serious-relationships-and-fleeting-ones.md b/content/writing/serious-relationships-and-fleeting-ones.md
@@ -0,0 +1,83 @@
+---
+title: "진지한 관계, 스쳐지나가는 가벼운 관계"
+date: 2025-10-17
+tags: []
+---
+
+언제부터 인지는 모르겠지만 어렸을때 부터 그렇게 생각했었고 그랬었다.
+
+친구나 가족에게는 정말 진지하고 솔직한 대화를 하지 않았고 오히려 금방 지나갈 사람, 한번 보고 말 사람들에게 오히려 내 본모습을 보여주고 솔직한 대화를 즐겨했다.
+
+왜일까?
+
+처음부터 이랬던것 같기도하다.
+
+초중고딩때도 학년이 바뀔때마다 반이 달라지고 구성원이 달라져서 좋았다. 아쉬워하는 친구들도 물론 많았지만 적어도 나는 좋았다.
+
+그렇다고 그 친구들이 싫었던것도 아니고 친구가 없던것도 아니다.
+
+오히려 반장도 하고 전교부회장도 나갈 정도로 교우관계를 적극적으로 임했다.
+
+학년이 바뀔때마다 아쉬운 마음보다 새로 사귈 친구들에 대한 기대와 두근거림으로 가득했다.
+
+친구들이 들으면 섭섭하겠지만 어쩔 수 없는 사실이다.
+
+사실 이부분에 대해 진지하게 생각해 본적은 없었다. 적어도 여태까지는.
+
+그냥 무의식적으로 그렇게 지내왔었고 그렇게 30대가 되었던 것이다.
+
+이 부분에 대해 깊게 생각하게 된 계기는 사회생활을 하다보니 나같은 케이스가 드물다는 것이였다.
+
+그래서 고민을 해보았다. 나는 왜 여태까지 이렇게 살아왔을까?
+
+단순히 인간관계에 대한 부담감 때문에? 그냥 흥미가 떨어져서? 아니면 인간관계에 상처를 받아서?
+
+음... 사실 다 이유에 포함된다.
+
+처음에는 흥미가 떨어졌던게 맞다. 학교라 해봤자 한 동내에 있는 또래 애들이였고 동내 애들이라 해봤자 다 거기서 거기라 흥미가 금방 떨어졌다.
+
+그리고 어리면 어릴 수록 부딪힐 일이 많고 저돌적으로 돌진하는 교우관계이다 보니 금방 친해지고 금방 흥미가 떨어졌다.
+
+두번째는 사춘기 이후 즉, 중학생 이후 부터이다. 몸은 아직 어리지만 머리는 좀 큰 학생으로써 이제 슬슬 조심히 접근하고 본인의 가치관과 맞지 않는 친구는 거르면서 서클이 생겨났다.
+
+그러다 보니 금방 친해지지는 못하고 학년이 끝날때까지 많은 대화를 하지 못하는 친구도 종종 있었다.
+
+그래서 흥미가 떨어졌다기 보다는 한정된 교우관계에서 오는 부담감이 컸다.
+
+같은 무리에서 잘 지내더라도 수가 틀리면 그 무리에서 배제되기 쉬웠고 전처럼 쉽게 다른 무리에 들어가기는 힘들었다.
+
+그러다 보니 최대한 그 무리에 맞춰 행동해야 했고 나를 제단해야했다.
+
+세번째는 갓 성인이 된 이후부터이다. 성인이 된 후로 한 동내에서 봐왔던 친구들이 아닌 정말 많은 지역과 나와다른 배경에서 자라온 사람들을 접하게 되었다.
+
+정말 다양하고 많다보니 흥미가 떨어질 일도 없었고 부담감을 느낄 새도 없었다.
+
+하지만 하나를 간과했다. 별의별 배경을 가지고 있는 사람들이라 그런지 그만큼 알게 모르게 상처를 받을 일도 많았다.
+
+진지하고 애뜻한 관계가 될 수록 상처는 더 크게 돌아왔다.
+
+그리고 그 상처를 치료하느라 감정을 소모하는 내 자신이 너무 피곤했다.
+
+그렇게 나라는 존재는 바람과 같은 존재, 바람과 같이 잠깐 있다 사라지는 그런 인간관계를 더욱더 선호하게 되었다.
+
+나를 전혀 알지 못하는 사람일 수록 접근하기 쉬웠고 그런 사람들과 대화하는게 좋았다.
+
+내 배경을 전혀 알지 못하는 사람일 수록 좋다. 나를 오늘 처음 만난 사람일 수록 좋다.
+
+여행에서 처음 본 사람, 일을 하면서 처음 본 사람, 네트워킹 파티에서 처음 본 사람.
+
+시덥지 않은 얘기를 하기 딱 좋은 상대이다.
+
+새로운 인연은 언제나 두근거린다. 부담감도 없고 상처를 줄 일도 상처를 받을 일도 없다. 맞지 않으면 그 날부로 보지 않으면 되고 만약 좋은 기회가 된다면 또 볼 수 있지 않을까 하는 기대감도 있다.
+
+처음보는 자리에서 내 본모습을 거리낌 없이 보여주면 흥미를 가지는 사람도 있고 조용히 자리를 떠나는 사람도 있다. 흥미를 가진 사람도 몇번 보다보면 불편해하고 떠나가는 사람도 있다.
+
+그렇게 내 주변에는 나를 진정으로 아는 사람만 남게 되었다. 내가 언제 없어서도 이상하게 생각하지 않을 인물들.
+
+아쉽게도 소속감을 가져본적이 없다. 그래서 소속감에서 오는 행복을 알지 못한다.
+
+하지만 아직 30년 밖에 삶을 살아 보지 못해서 소속감에서 오는 행복을 느낄 기회는 많다.
+
+그런 날이 오기를 기대하면서 오늘도 나는 여자친구와 함께 드라마를 본다.
+
+아, 가족은 이 세개 모두 포함된다.
diff --git a/content/writing/the-moment-you-force-a-reason-for-life-you-lose.md b/content/writing/the-moment-you-force-a-reason-for-life-you-lose.md
@@ -0,0 +1,29 @@
+---
+title: "삶에 이유를 만들려 하는 순간 진다"
+date: 2025-10-17
+tags: []
+---
+
+삶이라는 과정에 이유를 만들려 하지마라 생각 조차 하지마라
+
+생각 하려 하는순간 온갖 자기합리화와 변명을 생각하게 된다.
+
+삶이라는 과정에 브레이크를 거는것과 같다.
+
+그냥 가도록 냅둬라.
+
+그래도 한번쯤 멈춰서 방향을 살펴봐야 하지 않냐고?
+
+아니? 멈추는 순간 수많은 선택지에서 고민하게 되고 고민하는 동안 시간만 낭비하게 된다.
+
+어떤 선택을 하든 결국 길은 하나다.
+
+그냥 가라.
+
+다른 길을 가게되는 본인의 모습을 상상하면 끝도 없다. 답도 없다.
+
+후회와 미련만 남게 된다.
+
+인지부조화가 오게 된다. 인지부조화가 시작된 순간 현재 본인에 대한 자기변명과 자기합리화가 시작된다.
+
+본인이 맞는 길을 가고 있다는 심리적 보상심리 때문에.
diff --git a/content/writing/tso.md b/content/writing/tso.md
@@ -0,0 +1,68 @@
+---
+title: "TSO (TCP Segmentation Offload)"
+date: 2025-12-07
+tags: []
+---
+
+# A. Summary
+
+현대의 고성능 네트워크 환경(1Gbps ~ 100Gbps) 에서 CPU의 처리 속도는 네트워크 대역폭을 따라가기 벅찬 경우가 많다. **TSO (TCP Segmentation Offload)**와 **LSO(Large Send Offload)** 는 이러한 병목 현상을 해결하기 위해, CPU가 담당하던 패킷 처리 부하를 네트워크 인터페이스 카드(NIC)로 이관(Offload)하는 핵심 기술이다.
+
+## B. 기술적 배경: MTU의 제약과 CPU의 부하
+
+### B.1. MTU (Maximum Transmission Unit) 의 한계
+
+이더넷(Ethernet) 환경의 표준 전송 단위인 MTU는 기본적으로 1,500 Byte로 제한된다. 아무리 거대한 데이터(Ex. 4GB 파일) 를 전송하더라도, 실제 물리 계층(Physical Layer) 을 통과할 때는 반드시 1,500 Byte 크기의 작은 패킷으로 쪼개져야 한다.
+
+### B.2. 레거시(Legacy) 방식의 문제점
+
+TSO/LSO 기술이 적용되지 않은 환경에서는 이 "쪼개는 작업(Segmentation)" 을 운영체제의 커널(CPU) 이 직접 수행했다.
+
+- 오버헤드 발생: 64KB 데이터를 전송하려면 CPU는 약 44번의 분할 작업, 헤더 생성, 체크섬(Checksum) 계산을 수행해야 한다.
+- 인터럽트 폭증: 각 패킷을 전송할 때마다 CPU와 NIC 간의 통신(Interrupt) 이 발생하여 시스템 전체 성능 저하를 유발한다.
+
+## C. 상세 작동 원리 (Mechanism)
+
+TSO/LSO는 **도매(Wholesale) 와 소매(Retail)** 의 원리로 작동한다. CPU는 데이터를 큼직한 덩어리(도매)로 넘기고, NIC가 이를 잘게 나누어(소매) 전송한다.
+
+### C.1. 프로세스 비교
+
+| 구분 | Non-offload (CPU 처리) | Offload Enabled (TSO/LSO) |
+| --- | --- | --- |
+| 데이터 전달 | CPU가 1,500 Byte씩 잘라서 44번 NIC에 전달 | CPU가 64KB 통데이터를 단 1번 NIC에 전달 |
+| 헤더 / 체크섬 | CPU가 44회 직접 연산 및 부착 | NIC 칩셋이 하드웨어적으로 고속 연산 및 부착 |
+| 시스템 부하 | CPU 사용률 급증, 버스 대역폭 낭비 | CPU Idle 상태 유지, 효율적 대역폭 사용 |
+
+### C.2. 데이터 흐름도
+
+1. Application: 대용량 버퍼 생성
+2. TCP/IP Stack (OS): 데이터를 쪼개지 않고, 거대한 패킷(Super-packet) 상태로 NIC 드라이버에 전달
+3. NIC (Hardware): DMA(Direct Memory Access) 를 통해 데이터를 읽어 들인 후, 내부 로직을 통해 MTU 사이즈에 맞춰 고속으로 분할(Segmentation) 하여 전선(Wire) 으로 송출
+
+## D. 용어의 기술적 구분 (Terminology)
+
+- TSO (TCP Segmentation Offload)
+ - 주로 Linux/Unix 진영에서 사용하는 용어
+ - 명칭 그대로 TCP 프로토콜 의 패킷 분할을 오프로딩하는 것에 특화
+- LSO (Large Send Offload)
+ - 주로 Windows/Microsoft 진영 및 하드웨어 벤더에서 사용하는 용어
+ - TSO와 동일한 개념이나, TCP 외의 프로토콜까지 포함하는 더 포괄적인 의미로 쓰이기도 한다. (LSO V1, V2 등으로 버전이 나뉨)
+- GSO (Generic Segmentation Offload)
+ - 소프트웨어적 대안 기술. NIC가 하드웨어적으로 TSO를 지원하지 않거나 불안정할 때 사용
+ - 패킷 분할을 NIC 전달 직전 까지 최대한 미루어(Deferred Segmentation), 커널 네트워크 스택 내에서의 순회 비용을 줄이는 기술
+
+## E. 장단점
+
+### E.1. Pros
+
+1. Throughput(처리량) 향상: CPU 병목이 사라져 10Gbps 이상의 고대역폭을 온전히 활용 가능
+2. CPU 효율성 증대: 절약된 CPU 자원을 애플리케이션 처리에 할당 가능
+
+### E.2. Cons
+
+1. 패킷 캡처의 혼란
+ 1. tcpdump 나 wireshark 로 패킷을 캡처하면 1,500 Byte보다 훨씬 큰(Ex. 64KB) 비정상적인 패킷이 잡힌다. 이는 캡처 시점이 NIC 처리 이전 이기 때문이며, 실제 네트워크 장애로 오인하기 쉽다.
+2. 마이크로 버스트(Micro-burst) 및 지연
+ 1. NIC가 거대한 데이터를 한 번에 처리하려다 보니, 순간적으로 트래픽이 폭주(Burst) 하여 버퍼가 꽉 차는 현상이 발생할 수 있다. 민감한 실시간 통신(VoIP, 금융 트레이딩) 에서는 오히려 미세한 지연(Latency) 을 유발하기도 한다.
+3. 하드웨어 버그 및 호환성
+ 1. 일부 NIC 칩셋의 경우, TSO 처리 로직의 결함으로 인해 DMA Hang(시스템 멈춤) 이나 데이터 커럽션(Corruption) 이 발생할 수 있다. 이 경우 TSO를 비활성화하는 것이 표준 대응 절차다.
diff --git a/content/writing/var-log-promox-meogtong-troubleshooting.md b/content/writing/var-log-promox-meogtong-troubleshooting.md
@@ -0,0 +1,89 @@
+---
+title: "Promox 먹통 Troubleshooting"
+date: 2025-12-03
+tags: []
+---
+
+몇달 전부터 계속해서 Promox 와 Promox에서 돌아가고 있는 VM들의 서비스에 접근이 안되는 문제가 발생했다.
+
+정확한 이슈는 다음과 같다.
+
+- Promox 서버 자체에 GUI, SSH 접근 불가
+- Promox 서버 내의 VM과 서비스들 접근 불가 및 네트워크 오류
+- 웹 애플리케이션은 클라우드 터널링 떨어진것 확인
+- 위와 같은 이슈가 발생하면 물리적 전원 버튼을 눌러 시스템을 재부팅 하고 VM 서버들을 재부팅하면 접근 정상화, 서비스 정상화가 됨.
+- 하지만 몇일 지나지 않아 똑같은 이슈가 지속적으로 발생
+
+Promox 에서[“journalctl -e” or “journalctl -b -1 -e”](https://openpesto.com/etc-systemd-journalctl/)명령어로 시스템이 죽기직전 로그를 확인해본 결과 로그 내에 별다른 이슈 없었음.
+
+그렇다면 시스템이 한순간에 Freeze 된 상태가 된것으로 판단.
+
+서버에서 사용중인 **Intel I219-LM 칩셋 랜카드와 리눅스 기본 드라이버인 e1000e 사이에 유명한 TCP Offload(부하 분산) 버그가 있는것으로 확인.**[**[1]**](https://bugzilla.kernel.org/show_bug.cgi?id=118721&ref=openpesto.com)[**[2]**](https://forum.proxmox.com/threads/intel-nic-e1000e-hardware-unit-hang.106001/?ref=openpesto.com)[**[3]**](https://sourceforge.net/p/e1000/bugs/572/?ref=openpesto.com)
+
+해당 버그를 쉽게 설명하면 랜카드가 "내가 CPU 대신 이 데이터를 처리해 줄게!"라고 나섰다가, 감당하지 못하고 그냥 뻗어버리는 현상.
+
+원인은 해당 로그에서 확인할 수 있었음.
+
+```bash
+$ lspci | grep -i ethernet
+Dec 02 23:21:58 devastator kernel: e1000e 0000:00:1f.6 eno2: Detected Hardware Unit Hang:
+```
+
+- 해석: 리눅스 커널이 eno2라는 이름의 네트워크 장치(인텔 I219-LM)가 "하드웨어적으로 멈췄음(Hang)"을 감지
+- 상황: 랜카드가 멈추니 네트워크가 끊기고, Tailscale(VPN)도 연결이 끊어지며(controlhttp: context aborted), 시스템 전체가 네트워크 불능 상태에 빠진 것
+
+### 해결 방법 (TCP Offload 끄기)
+
+랜카드가 무리하지 않도록 "너는 그냥 데이터만 전달해, 처리는 CPU가 할게"라고 설정을 바꿈.
+
+A. ethtool 설치
+
+```bash
+$ apt update && apt install ethtool -y
+```
+
+B. TCP Offload 끄기
+
+```bash
+$ ethtool -K eno2 tso off gso off
+```
+
+C. 재부팅 해도 유지되도록 설정 (영구 적용)
+
+C.1 네트워크 설정 파일 열기
+
+```bash
+$ vim /etc/network/interfaces
+```
+
+C.2 iface eno2(네트워크 인터페이스 이름) inet ... 라고 적힌 부분 찾기
+
+C.3 해당 설정 블록 바로 아래줄에 다음 내용 추가 (들여쓰기)
+
+```bash
+ $ post-up ethtool -K eno2 tso off gso off
+```
+
+Ex)
+
+```plaintext
+auto eno2
+iface eno2 inet manual
+ post-up ethtool -K eno2 tso off gso off
+```
+
+### 요약
+
+해당 이슈는 **하드웨어(Intel I219)가 주장하는 스펙**과 **리눅스 드라이버(e1000e)가 보내는 데이터**간의 불일치로 인해 발생하였다.
+
+1. 원인: 리눅스 커널은 CPU 부하를 줄이기 위해 패킷 분할(Segmentation) 작업을 랜카드에 떠넘긴다. (TSO: TCP Segmentation Offload)
+2. 버그: 하지만 I219 칩셋의 특정 리비전은 버퍼 관리 미흡으로 인해, 과도한 TSO 요청이 들어오면 DMA 컨트롤러가 꼬이면서 칩셋 자체가 멈춰버린다. (Hang)
+3. 해결: ethtool -k eno2 tso off gso off 명령어는 커널에게 "이 랜카드의 TSO, GSO 기능을 사용하지 말고 CPU가 직접 패킷을 쪼개서 보내" 라고 지시하는 것
+
+## References
+
+[1] [https://bugzilla.kernel.org/show_bug.cgi?id=118721](https://bugzilla.kernel.org/show_bug.cgi?id=118721&ref=openpesto.com)
+
+[2] [https://forum.proxmox.com/threads/intel-nic-e1000e-hardware-unit-hang.106001/](https://forum.proxmox.com/threads/intel-nic-e1000e-hardware-unit-hang.106001/?ref=openpesto.com)
+
+[3] [https://sourceforge.net/p/e1000/bugs/572/](https://sourceforge.net/p/e1000/bugs/572/?ref=openpesto.com)
diff --git a/content/writing/why-we-assume-the-present-will-last.md b/content/writing/why-we-assume-the-present-will-last.md
@@ -0,0 +1,37 @@
+---
+title: "왜 인간은 현재 상황이 지속될꺼라 착각할까?"
+date: 2025-10-16
+tags: []
+---
+
+마치 사람들은 영생을 사는것처럼 행동한다.
+
+우리 모두 죽음을 향해 달려가고 언제가는 죽게 된다.
+
+언제 죽을지는 아무도 모른다. 내일 죽는 사람도 있을꺼고 1분뒤에 죽는사람도 있을꺼고
+
+근데 사람들은 마치 영생을 사는것처럼 지금 현재 상황이 변하지 않고 지속될 것처럼 해동한다.
+
+우리에게 주어진 시간은 무한하지 않다. 영생도 없고 전생도 없다.
+
+평균적으로 인간에게 주어진 시간은 60년 정도 이다.
+
+근데 우리는 마치 본인이 500살 1000살 살수 있는것 마냥 행동한다.
+
+우리에게 주어진 시간은 생각보다 적다.
+
+따라서 우리는 이 주어진 시간에 최대한 하고싶은거 누릴수 있는거 다해봐야 한다.
+
+계속 미루다보면 확률적으로 못하고 죽을 가능성만 더욱더 커질 뿐이다.
+
+여행이든 연애든 고백이든 하고싶은 일이든 뭐든.
+
+자식뿐만 아니라 타인이 본인의 삶은 대신해서 살아줄 수 있다는 착각은 하지마라.
+
+본인에게 주어진 시간은 공평하게 본인에게만 주어져있고 쓰는것도 본인이다.
+
+그 누구도 본인의 삶을 살아가 줄 수는 없다.
+
+2분 지났다.
+
+나 뿐만 아니라 우리 모두의 삶에서 2분이 지나가 버렸다. 이미 지나간 시간은 다시 돌아갈 수는 없다.
diff --git a/content/writing/will-the-next-revolution-succeed-part-1.md b/content/writing/will-the-next-revolution-succeed-part-1.md
@@ -0,0 +1,189 @@
+---
+title: "다음 혁명은 성공할까? (1부)"
+date: 2025-11-14
+tags: []
+---
+
+결론부터 말하자면 아직은 아니다.
+
+향후 몇년 안에 큰 분기점, 특이점이 올것은 명확해 보인다.
+
+분기점을 맞이하면서 큰 혼돈이 발생하겠지만 쓰나미를 막을 수는 없을 것으로 보인다.
+
+늦추는건 가능하더라도 막는건 불가능해보인다.
+
+이미 추세가 기울엇고 계속해서 힘을 보태고 있는 상황이다.
+
+아쉽게도 현재 시스템은 너무 오래되었다.
+
+즉, 이미 낡았고 단점들이 더욱 명확해 졌으며 무너지기 쉬운 젠가의 마지막 단계라 보여진다.
+
+다음의 새로운 이념 체제는 어떻게 될까?
+
+시스템은 어떻게 돌아가게 될까?
+
+큰 파도에 휩쓸려 빠지지 않고 흐름에 맞겨 잘 나아가려면 어떻게 대비해야 할까?
+
+우리가 할 수 있는건 최대한 예측하고 예측에 맞게 준비하는 것 뿐이다.
+
+그래야 예기치 않은 상황이 와도 유연하게 대처할 수 있는 유일한 방법이기 때문이다.
+
+무턱대로 예측하고 대비할 수는 없으니 최대한 예측의 정확도를 높여보자.
+
+현재 추세는 두가지 큰 물줄기로 보인다.
+
+첫번째는 AI
+
+두번째는 자본주의, 민주주의에 대한 불만
+
+먼저 AI는 거스를 수 없을 것으로 보인다.
+
+모든 기술의 발전은 인간을 대신하고, 인간이 귀찮아 하는 노동력을 대신하기 위한 목적으로 발전이 되었기 때문에 AI 또한 그 기술발전의 연장선이라 보인다.
+
+그렇다면 자본주의와 민주주의에 대한 불만은 어쩌다가 발생하고 발전했을까?
+
+아이러니하게도 기술의 발전으로 인해 이러한 불만이 야기되고 계속해서 발전해 나가고 있다.
+
+이해가 되지 않을 수 있다.
+
+인간의 유일한 장점인 원인과 결과를 연관지으고 관계성을 중요시 하는 특성을 잘 이용한다면 쉽게 이해할 수 있다.
+
+먼저 기술의 발전을 이룩하면서 여성의 사회적 진출이 이뤄졌다.
+
+생산량이 기존 인구의 2배가 되었고 증가된 생산량에 따라 기술의 발전이 폭발적으로 빨라지게 되었다.
+
+또한, 인구의 수명도 크게 증가하기 시작했다. 이는 곳 평균 수명이 높아지고, 정년이 길어지고, 은퇴가 늦어지는 원인이 된다.
+
+다시한번 생산인구가 약 2배 더 증가했다.
+
+즉, 기술의 발전 또한 약 2배 더 증가했다.
+
+하지만 여기서 간과한게 있다. 이 엄청난 기술적 성장은 단 몇 십년 안에 발생된거다.
+
+50년도 체 되지 않았다.
+
+하지만 인간의 인지와 본질은 기술적 발전 속도를 못따라가고 있다.
+
+인간의 정신과 시스템은 과거에 머물러 있는데 기술 속도만 계속해서 빨라지고 있는 것이다.
+
+우리는 아직도 가부장적 시스템과 남자의 역할, 여자의 역할이라는 시스템, 결혼 제도 등 다양한 과거 시스템에 머물러 있는데 기술만 앞질러 가고 있다.
+
+여기서 큰 괴리감이 발생하기 시작한다.
+
+여성의 소득이 크게 증가하고 남자보다 더 많은 소득을 버는 여자도 있지만 우리는 남자가 여자보다 더 능력이 뛰어 나야 하고 책임지는 가장이 되는게 멋진 남성 상이라 여기고 있다.
+
+하지만 현실은 다르다. 현실에서 그런 남녀는 찾기 점점 힘들어지고 여성또한 사회적으로 인정받고 싶은 욕구가 더 높은 사람들이 많아지고 있는 추세이다.
+
+이런 현실과 사회가 바라는 이상의 괴리속에서 출산율은 당연하게도 떨어질 수 밖에 없다. 앞서말한 그런 이상적인 남녀커플은 점점 줄어들고 있고 그런 커플이 사회적으로 당연하고 멋져보이는 인식은 더욱더 연애에 접근하기 어려운 벽 역할을 하고 있다.
+
+또한, 개인의 자유와 개성을 더욱더 갈망하는 사회인 것도 사회 시스템을 무너트리는데 한몫하고 있다.
+
+우리는 자유롭다고 느끼고 있지만 사실 정말 자유를 느끼는 자는 소수이다.
+
+느끼는 것과 진짜 자유를 가지는 것은 큰 차이이다.
+
+예전에는 우리가 비교하고 우리의 가치를 확인하는 방법은 우리 주변의 사람들, 친구, 동료 뿐이였다.
+
+하지만 계층간의 경계가 무뎌지고 시공간의 경계가 허물어지면서 우리는 지금 옆에 있는 사람들 뿐만아니라 서로 본적도 없는 70억 명의 인구와 비교하고 경쟁하게 되었다.
+
+인간은 아이러니 하게도 자유를 추구하지만 준비되지 않은, 명확하지 않은 자유 앞에서는 스트레스를 받고 불안감을 느낀다.
+
+과거 신분 사회에서는 아무리 노력하고 길고 날고 해봐야 해당 계층 안에서 사람을 만나고 소속을 가지고 결혼을 하며 시스템안의 쳇바퀴 처럼 살아갈 수 있었다.
+
+하지만 지금은 내가 좀 더 노력하면 다른 곳에 갈 수 있고 다른 사람들과 어울릴 수 있으며 자신의의 비루했던 과거를 바꿀 수 있다는 희망이 보일 만큼 눈높이가 높아지게 되었고 욕심을 부릴 수 있는 환경이 되었다.
+
+특히, 우리는 정말 못사는 나라에서 선진국의 반열에 오르기 까지 정말 많은 속물주의에 알게모르게 노출이 되었었다.
+
+우리는 부모님과 어른들로 부터 대학가고 좋은 직장을 얻으면 미래의 배우자가 바뀔 수 있다 라는 말을 농담반 진담반으로 듣고 자랐다.
+
+사실 지금도 마찬가지이다. 다들 어렴풋이 마음 한켠에 지금 내가 여기서 더 노력하면 더 멋있고 이쁘고 잘나가는 배우자를 만날 수 있을것 이라는 욕심으로 인해 연애할 시간마져 할애해 가며 자신의 청춘을 버리고 있다.
+
+물론 지금은 덜한것 같지만 지금의 퐁퐁남, 영포티라고 불리우는 세대들은 이런 환경에 계속해서 노출이 되었던건 사실이다.
+
+그래서 그들을 어느정도는 이해를 할 수 있다.
+
+본인들은 어려서부터 열심히 노력하면 나중에 이쁘고 젊고 참한 아내 얻을 수 있다는 부모님의 말을 철썩 같이 듣고 열심히 공부해서 대학에 입학 후 멋진 회사에서 자신의 자존감을 내려놓으면서 까지 열심히 달려 왔는데 현실은 이미 같이 사회생활하고 남부러울 것 없는 아내에게 존중받지 못하고 집에서 눈치보고 있으면 자신이 여태까지 달려왔던 것을 부정하기에는 너무 힘드니 밖에 있는 딸뻘되는 여성한테 치근덕 되는건 어쩔 수 없다고 느껴진다.
+
+현실의 결핍을 채우려고 하는건 인간의 본능이니까.
+
+결국 사회가 그들을 그렇게 만든것이다.
+
+물론 그들의 청년기는 급격한 경제성장으로 인해 경제적으로 안정되고 꽤 괜찮은 삶을 살았을 수 도 있다. 하지만 본인이 가진게 경제적인 것 뿐이라면 그것으로 밖에 어필 할 수 없는 세대가 되어버린 것이다. 결국 본인의 내면은 채우지 못하고 남 부러울 것 없는 가정, 위치를 위해 본인을 갉아먹고 이제와서 보상을 바라는 것으로 해석된다.
+
+그들은 가난했던 집안과 부모님에게 효도하기 위해 한평생을 달려왔고 가정을 부양해야할 책임이 있다고 뼈저리게 느끼고 남성성, 여성성을 강요받은 세대라 급격히 바뀐 현실을 인지할 시간 조차 없었다. 그렇게 그들은 인지부조화가 온 상태로 영포티, 퐁퐁남이라는 단어로 모든 세대에게 비난을 받고 있다.
+
+나는 그들을 이렇게 지칭하고 싶다.
+
+“인지부조화 세대”
+
+그럼 우리는 인지부조화에 걸리지 않게 대처하는 방법은 없을까?
+
+일단 사회적인 합의와 인지가 현실과 일치하는 방법이 최선이다.
+
+현재 일어나는 사회적 시스템의 붕괴는 이러한 사회적 합의와 현실이 일치하지 않은 문제에서 발생되고 있는 것이다.
+
+과거에 머물러 있고 천천히 바뀌고 있는 사회적 합의는 급격히 발전하고 있는 현실을 점점 따라잡지 못하고 있고 이로 인한 괴리감이 사람들을 혼란에 빠트리고 시스템을 망가트리고 있다.
+
+그럼 답은 둘중에 하나이다.
+
+사회적 합의를 급격하게 발전하고 있는 현실에 맞출 것인가?
+
+현실을 사회적 합의의 맞게 천천히 발전 시킬 것인가?
+
+지금 당장으로 서는 후자가 더 해결책에 가까워 보인다.
+
+새로운 다음 세대의 인간이 생기지 않는 이상 과거에 머물러 있는 현재 사람들에 맞춰 기술을 발전 시키는 것이 안정되고 사회적 붕괴없이 나아 갈 수 있다고 믿는다.
+
+급격히 바뀌는 현실을 계속해서 따라가기에는 현재 인간의 신체적 한계로는 어렵다.
+
+인간의 본능과 편견, 인지를 한순간에 바꾸는건 힘들고 적어도 몇백, 몇천년이 걸리기 때문이다.
+
+가부장 시스템만 봐도 인간이 태어나면서 부터 몇천년동안 함께한 시스템이다.
+
+이걸 지금의 현실과 비교하다보니 괴리감이 크고 그렇다고 쉽게 바꾸기는 어려운게 현실이다.
+
+현재 시스템을 안정적으로 계속해서 이어 나가려면 결혼이라는 제도와 아이를 낳는건 필수 적이기 때문에 명확한 해결책이 없는한 인간의 현실을 받아들이는 수 밖에 없다고 생각한다.
+
+해결책은 이미 몇몇 해결책을 제시한 사람들이 있는데 이런 소신발언을 한 사람들의 말을 비난하고 무시하지 않고 진지하게 경청하는 것도 좋은 방법이다.
+
+사실 소신발언도 아니다. 모두가 해결책을 알고 있지만 후폭풍이 무서워서 말을 하지 않는것 뿐이다.
+
+난 이런 사회가 너무 무섭다. 발언할 자유가 없는 사회. 다수가 독제하는 사회는 정말 무섭다고 생각한다.
+
+다들 있는 사실을 그대로 바라보지 못하고 상대방이 기분나쁠 수 있으니, 불편한 사람들로 인해 모두가 침묵하는 사회는 결국 자연스럽게 독제를 불러온다.
+
+결국 필자가 블로그 내내 말했듯이 본질을 애써 무시하는 사회는 결국 무너질 수 밖에 없다는 이야기를 또 할 뿐이다.
+
+각설하고 필자도 다른 소신발언을 한 인물들과 같은 생각을 가지고 있다.
+
+해당 발언들에 대해 추가적으로 덧붙인것 뿐이다.
+
+결국 현재 시스템을 안정적으로 유지하고 이어 나가려면 여성의 사회적 진출을 장려하는게 아니라 자유에 맞기고 오히려 전업주부에 대한 혜택을 늘려 전업주부에 대한 인식을 높이는게 현재로서는 최선의 대안책이라 느껴진다.
+
+결국 남녀가 사랑을 하고 연애를 하고 아이를 낳으려면 여성에 대한 눈높이를 낮출 필요가 있다.
+
+결국 여성은 자신보다 더 뛰어난 사람과 연애하고 안정적인 가정을 이루기 위해 나이가 많아도 능력이 좋은 남성을 찾는다는건 변함없는 사실이다.
+
+부정할 필요도 없다. 2000년 인류 역사가 그 증거를 대변해준다.
+
+아, 물론 예외는 존재한다는걸 부정하지는 않는다.
+
+어디까지나 예외가 아닌 다수에 의한 보편적이고 일반적인 가치 기준에서 말하는 것 뿐이다.
+
+반대로 남성또한 여자들의 본질적인 심리를 알아서 능력있고 안정적인 가정을 이룰 수 있는 것을 보장하고 증명하는 남자가 더 좋은 여자를 쟁취할 수 있다는 점에서 인류가 걸어왔음을 부정하지 않는다.
+
+하지만 현재는 이러한 인간의 신체적, 정신적 본질을 무시하고 외면한체 달려왔다.
+
+이제야 돌아보고 문제점을 인식한 것 뿐이다.
+
+물론 문제점을 인식하고 해결책도 알고 있지만 이미 멀리 와버려서 돌아가기에는 무서워 시도조차 하고 있지 않는 것 뿐이다.
+
+계속해서 이러한 상황을 애써 무시하고 달린다면 결국 몇년안에 큰 혁명, 분기점이 필연적으로 올것이다.
+
+한번은 이러한 현실과의 괴리감을 맞출 필요가 있기 때문이다.
+
+마치 이미 많이 어긋난 오래된 시계를 현재 시간으로 맞추는 것처럼 꽤 어려운 과정이 될 것이다.
+
+그럼 다음 혁명은 어떻게 올것이고 어떤 모습을 할까?
+
+[2부에서]
diff --git a/content/writing/work-is-finding-shoes-that-fit-me.md b/content/writing/work-is-finding-shoes-that-fit-me.md
@@ -0,0 +1,21 @@
+---
+title: "일은 나에게 맏는 신발을 찾는 과정이다"
+date: 2025-10-16
+tags: []
+---
+
+사회생활을 하다보면 정말 일을 잘하시고 열심히 하시는 분들이 있다.
+
+그런분들을 가만히 지켜보면 정말 본인의 일을 좋아하시고 즐거워 하신다.
+
+해당 일의 즐거움 비율에 따라 그일을 더 잘하고 못하고가 갈라지는것 같다.
+
+즐거움률을 100이라 했을때 90% 즐거움을 느끼는 사람이 60% 즐거움을 느끼는 사람보다 더 일을 잘하고 높은곳에 위치해 있다.
+
+물론 간혹가다 해당일에 크게 즐거움을 못느끼지만 어느정도 위치에 올라와 있는 사람도 있는데 그래도 한계가 있다. 대부분 더이상 위로 올라가기도 힘들고 그 일을 오래 지속적으로 일하기는 무리를 느낀다.
+
+우리가 한평생 할 수 있는 일을 찾는것은 신발을 고르는 것과 같다. 본인한테 딱 맞는 신발을 찾는 과정이다. 사이즈가 딱 맞아 편하게 오래 신을 수 있는 신발. 불편하지만 디자인이 좋은 신발이 있지만 결국 평생 나와 함께하고 자주 신게 되는건 나에게 딱 맞는 편한 신발이다.
+
+우리는 치수가 조금이라도 틀어지게 되면 신발이 커서 헐떡 거리기도 하고 신발이 작아 꽉 조여 발이 답답하고 아플 수 있다. 이런 신발은 아무리 디자인이 좋아도 오래 못신는다.
+
+결국 본인의 인생을 한평생 같이 걸을 수 있는 편안하고 오래된 신발을 찾아야 계속 걷고 뛸 수 있다.
diff --git a/content/writing/난나를진심으로만드는사람이랑일하고싶어.md b/content/writing/난나를진심으로만드는사람이랑일하고싶어.md
@@ -0,0 +1,85 @@
+---
+title: "난 나를 진심으로 만드는 사람이랑 일하고 싶어"
+date: 2026-06-30
+tags: []
+---
+
+
+
+겐바에 갔다온 후 주절히 주절히 떠드는
+
+공연장에서 아이돌들은 팬들을 진심으로 사랑하지 않지만 팬들은 아이돌을 진심으로 사랑하고 좋아하는 마음에서 응원하는데
+어떻게 보면 아이돌들은 팬들에게 거짓말을 하는거고 팬들은 아이돌들에 진심을 다하는 이러한 가식과 진실이 공존하고 부딪히는 공간이
+왜 흥미롭고 매력적이게 느껴지는지 내 감정에 의문이 들었다.
+
+사실 이 감정이 어떤 의미로 카타르시스를 느끼게 하는지는 정확히 알 수는 없었지만 어느정도 정리는 할 수 있었다.
+
+가식(연출된 환상)과 진실(순수한 감정)이 부딪히고 융합하는 공간은 특유의 말로 형용할 수 없는 매력이 있다.
+철저하게 기획된 환상과 날것의 진심이 공존하는 공간이 어떻게 폭발적인 에너지를 내뿜고 사람들을 매료시키는지 궁금했다.
+
+현실의 인간관계는 복잡하고 예측 불가능하며, 내 진심을 다한다고 해서 상대방이 똑같이 돌려주지 않을 때 오는 상처의 위험이 존재함.
+하지만 아이돌과 팬과의 관계는 철저히 기획된 가식(무대 위 퍼포먼스) 이라는 방파제가 있음.
+팬들은 이 관계가 통제되어 있다는 것을 무의식적으로 알기 때문에, 오히려 현실에서보다 훨씬 더 안전하게, 상처받을 두려움 없이 자신의
+모든 진심과 열정을 쏟아부을 수 있음.(가식이 역설적으로 진심을 해방시키는 역할을 하는 것)
+
+문학이나 영화에서 사용하는 자발적 불신 유예(Willing suspension of disbelief) 라는 개념이 있음.
+영화가 가짜라는 것을 알면서도 그 순간만큼은 진짜라고 믿고 눈물을 흘리는 것들을 뜻함.
+팬들 역시 깊은 곳에서는 아이돌의 "愛してる" 라는 말이 나라는 개인을 향한 것이 아님을 알고 있음.
+하지만 그 공간에 들어서는 순간, 사람들은 그 아름다운 거짓말에 기꺼이 동참하기로 합의함.
+왜냐면 무대 위의 상황은 연출일지라도, 그것을 보며 심장이 뛰고 위로를 받는 나의 감정 만큼은 100% 진짜이기 때문.
+
+공연장은 차갑고 치밀하게 계산된 자본과 기획의 결정체임. 조명, 동선, 멘트, 표정 하나까지 정교하게 만들어진 인공의 영역임.
+하지만, 무대 아래에서 뿜어져 나오는 팬들의 환호, 눈물, 열광은 전혀 통제되지 않은 날것의 감정임.
+가장 정교한 가식과 가장 폭발적인 진실이 한 공간 안에서 정면으로 부딪힐 때, 그 극단적인 대비는 일상에서는 절대 경험할 수 없는
+엄청난 카타르시스와 묘한 미학을 만들어냄.
+
+어떻게 보면 아이돌이 일방적으로 거짓말을 하는 것이 아니라, 팬들의 진심을 담아내기 위한 거대한 그릇을 연출해 주는 것에 가까움.
+비록 시작은 가식이었을지라도 그 공연장을 채우는 뜨거운 공기와 유대감은 하나의 거대한 진실이 됨.
+
+그렇기에 아이돌들의 애드리브가 더욱더 강조됨
+
+99%가 철저히 통제되고 계산된 완벽한 무대이기 때문에, 예기치 않게 튀어나온 1%의 통제되지 않은 진심(애드리브, 실수, 감정이 북받쳐 흘리는 눈물 등)
+이 더욱더 빛을 발함.
+늘 날것의 감정만 오가는 일상에서는 쉽게 무뎌질 수 있는 감정이, 가식이라는 어두운 배경 덕분에 진실이라는 스포트라이트를 강렬하게 받게 됨.
+
+또한, 나와 똑같이 감정을 느끼고 반응하는 한 인간과 교감하고 있다는 강렬한 유대감을 느끼게 됨.(완벽함 보다는 때로는 취약함이 사람의 마음을 더 깊게 여니까)
+
+하지만 직장이라는 공간도 이런 가식과 진심이 공존하지만 카타르시스를 느끼거나 흥미로운 공간이라고 인식하기 힘듦.
+
+결정적인 두 공간의 차이는 규칙과 목적이 완전히 정반대이기 때문.
+
+직장에서의 가식은 내 생존(월급, 커리어, 인사고과)을 위해 강제된 감정노동임. 상사에게 잘 보이거나 회사에 대한 충섬심은 선택이 아니라
+의무에 가깝기 때문에, 카타르시스는커녕 에너지만 고갈되는 억압적인 느낌만 줌.
+
+또한, 아이돌의 가식은 팬들에게 최고의 행복과 환상을 주기 위해 설계되었지만 직장에서의 가식은 갈등을 피하고, 조직의 톱니바퀴로서 무난하게 기능하기 위한 방어기제임.
+직장에서의 진심은 드러나는 순간 불이익으로 돌아옴.
+상사에 대한 분노, 업무에 대한 지루함, 솔직하지만 뼈아픈 비판 같은 진실은 프로페셔널하지 못하다는 평가를 받게 만듦.
+진심을 숨겨야만 안전한 공간이기 때문에 매력이 반감될 수밖에 없음.
+
+아이돌의 가식은 직장에서의 가식과 동일하게 아이돌 개개인들의 생존을 위해 행동하는 감정 노동이지만 그 감정노동을 소비하는 소비자가 어떻게 받아들이냐의 차이가 큼.
+직장에서는 동료나 파트너가 나에게 친절하게 웃어준다고 해서 "저 사람이 나를 진심으로 좋아하는구나" 라고 깊게 착각하거나 기대하지 않음.
+서로가 사회생활을 위해 연기하고 있다는 현실을 너무나 잘 인지하고 있음.
+
+하지만 공연장의 팬들은 아이돌들의 가식이 본인의 업무임을 이성으로는 알면서도, 그 순간만큼은 환상에 빠지고 싶어함.
+"나에게 완벽하고 아름다운 거짓말(환상)을 제공해 달라"는 합의가 성림된 공간이기 때문에, 그 가식은 비난받는 대신 열광적인 소비의 대상이 됨.
+
+그렇다면 결국 우리 모두가 생존을 위해 가식을 하고 있는거와 마찬가지인데 가식은 과연 옳은건가?
+
+결론부터 말하면 가식과 진심은 선과 악의 도덕적인 문제가 아닌 인간 스스로가 상처받지 않고 살아가기 위해 쓰는 도구임.
+
+가식은 본인의 약한 마음을 보호하고, 다른 사람과 불필요하게 부딪히는 걸 막기 위한 최소한의 방어선임.
+가공되지 않은 날것의 진심은 너무 무겁고 예리해서 때로는 날카로운 흉기가 되어 타인에게 폭력이 되고 깊은 상처를 줄 수 있음.
+
+### 거짓말은 최고의 사랑이니까
+누군가에게 완벽한 거짓말을 하려면 끊임없이 상대방을 관찰하고 상대가 무엇을 원하는지 알아내야 함.
+본인의 기분이나 감정은 철저히 숨긴 채, 오직 상대를 기쁘게 하기 위한 표정과 말을 만들어내야 함.
+나를 지키기 위한 이기적인 거짓말이 아니라, 팬들이 보고 싶어 하는 "가장 아름다운 꿈"을 깨뜨리지 않기 위해 내 모든 에너지를 쏟아붓는 이타적인 거짓말임.
+내 자아를 깎아내면서 까지 타인의 행복을 위해 기꺼이 완벽한 연기자가 되겠다는 다짐으로 그 자체로 이미 엄청난 헌신이자 사랑.
+
+작중 호시노 아이는 본질적인 사랑의 감정을 모르는 자신의 텅 빈 내면을 두려워함. 그래서 "팬들을 사랑한다"는 거짓말을 계속 내뱉으면서, 언젠가 이 거짓말이 진짜 진실이 되기를 간절히 바람.
+거짓말을 그저 속임수로 쓰는 게 아니라, 진심으로 가는 징검다리로 쓰고 있음.
+본인의 마음에 완벽한 진심이 없더라도, 흉내를 내고 계속해서 사랑한다고 말하다 보면 어느 순간 그 감정이 자신의 안에 진짜로 피어날 것이라는 믿음.
+즉, 거짓말은 진심에 닿기 위해 호시노 아니가 할 수 있는 가장 최선의 노력인 셈.
+그 거짓말을 무덤까지 가져간다면 결과적으로 가장 거대하고 무거운 진실과 다를 바 없음.
+
+
diff --git a/content/writing/서버리스환경맛보기수육.md b/content/writing/서버리스환경맛보기수육.md
@@ -0,0 +1,26 @@
+---
+title: "서버리스환경 맛보기수육"
+date: 2026-08-03
+tags: []
+---
+
+지속가능한 백수생활을 위해 기존에 온프레미스 환경에서 구동중이던 서비스들을 하나씩 서버리스로 마이그레이션 중이다.
+사실 서버리스라는 정의의 개념만 인지하고 있는 상태였고 환경이나 구축, 솔루션, 연동 같은 복잡한 구조까지는 알지 못하여 근 한달동안 짬짬히 공부하며 온프렘에서 서버리스로 마이그레이션을 진행하였다.
+
+온프레미스 환경이 관리, 통제 목적으로는 용이하다는 장점이 있어 개인적으로 온프렘 환경을 선호하지만 확실히 온프렘 환경은 인적, 환경적 여유가 있어야 지속가능한 시스템인거 같다.
+최근들어 온프렘 환경이 더 각박해진건 사실이다. 치솟는 전기값, 메모리, 스토리지 값, 퍼블릭 클라우드 인스턴스 비용(심지어 얘는 달러로 받아서 값은 2년전이랑 똑같은 모델인데 환율때문에 2배 더 비싸게 내고 있음), 도메인 비용 등등.
+보수적인 늙은이 엔지니어한테는 온프렘이 마음이 편하지만 어쩔수가 없음...
+
+덕분에 서버리스 환경과 솔루션들에 대해 공부할 수 있었고 생각보다 이론은 간단하지만 각각의 서비스에 맞는 솔루션들이 분산되어 있어 관리나 구성은 확실히 어려웠다.
+물론, AWS같은 대형 서비스를 이용하면 편하겠지만 아무래도 비용이...
+
+그래도 무료로 체험하고 공부할 수 있다는것 만으로도 큰 장점이라 도전해 보았고 제일 만만한 이 블로그 부터 서비리스 환경으로 옮겼고 아직까지는 큰 문제가 없는거 같다.
+
+사용한 솔루션은 다음과 같다.
+
+- Deploying: Vercel
+- Backend: Supabase
+- Redis: Upstash
+- DNS: Cloudflare
+- Domain: Spaceship
+- Version Control: Github
\ No newline at end of file
diff --git a/content/writing/안전하고자유로운인터넷을여행하는히치하이커를위한안내서.md b/content/writing/안전하고자유로운인터넷을여행하는히치하이커를위한안내서.md
@@ -0,0 +1,469 @@
+---
+title: "안전하고 자유로운 인터넷을 여행하는 히치하이커를 위한 안내서"
+date: 2026-07-20
+tags: []
+---
+> ℹ️ **예시값 안내**: 이 글의 **서버 IP(`203.0.113.10`)·위장 SNI 도메인(`www.example.jp`)·Reality 공개키·shortId·클라이언트 UUID는 모두 문서용 예시값**임(IP는 RFC 5737 문서 전용 대역 `203.0.113.0/24`, 도메인은 JPRS가 문서용으로 예약한 `example.jp`). 직접 구축할 때 본인의 실제 값으로 교체하면 됨. **개인키(privateKey/secretKey)는 절대 외부에 공개하지 말 것.** PoC에 나오는 **인증서·SNI 바이트는 이 예시 도메인 기준의 설명용 값**임(실제로는 본인이 고른 위장 도메인의 진짜 인증서가 그대로 반환됨). 반면 **출구 IP·성능 수치·트래픽 흐름은 실제 환경에서 뽑은 실측값**이며, 출구 IP는 수많은 사용자가 공유하는 Cloudflare WARP 대역이라 서버나 개인을 특정하지 못함.
+
+## 1. 자유롭게 인터넷을 사용하기 위한 여정
+
+동기는 단순함. **안전하고 자유롭게, 익명으로 인터넷을 사용하고 싶음.** 그래서 VLESS + Xray + Reality + WARP 를 활용한 프록시 시스템을 구축하였고 3개월간 운용한 내용을 바탕으로 작성하였음.
+
+한국의 인터넷 규제는 해가 갈수록 촘촘해짐 — 차단은 이미 DNS 수준을 넘어 **접속 목적지 이름(SNI)을 평문으로 들여다보는 단계**까지 왔고, 그 범위는 계속 넓어짐. 한국만의 이야기도 아님. 중국의 만리방화벽(GFW), 러시아의 "주권 인터넷"은 이 길을 훨씬 앞서감. 그리고 이 모든 곳에서 벌어지는 건 끝나지 않는 **창과 방패의 싸움**임 — 누군가 규제를 뚫으면 그걸 막는 새 규제가 생기고, 다시 그걸 뚫는 방법이 나옴. 여기서 힌트를 얻을 수 있음.
+
+일단, 현대의 검열이 어떻게 작동하는지부터 알아야 함. 요즘 검열은 "해당 도메인을 차단" 같은 단순 블랙리스트가 아님. ISP의 미들박스는 **트래픽의 "모양"을 보고 판단**함:
+
+1. **패킷 시그니처** — 프로토콜 고유의 지문을 식별함 (WireGuard 핸드셰이크는 고정 구조라 한 방에 걸림).
+2. **평문 SNI** — TLS 접속의 목적지 이름을 그대로 읽음.
+3. **행동 패턴** — 누구인지 모를 IP로 향하는, 브라우징 같지 않은 긴수명(long-lived) 세션을 이상치로 플래깅함.
+
+조짐은 이미 보임. 한국에서 자가 VPN을 며칠 돌려보면 **지속되는 TLS 세션만 골라서** 툭툭 끊기고(EOF), WireGuard·OpenVPN은 핸드셰이크 단계에서부터 막히곤 함. 대역폭 문제가 아니라, 트래픽의 "모양"이 평범한 사용자와 다르기 때문임.
+
+그래서 목표는 명확함.
+
+> **"검열자가 내 트래픽을 봤을 때, 그냥 평범한 사람이 유명 사이트를 HTTPS로 보는 것과 1비트도 구분되지 않게 만든다."**
+
+이 "구분 불가능성"을 만들어주는 게 **Reality**임. 그리고 여기에 **누가 어디서 접속하는지**까지 지우려고, 최종 출구를 **Cloudflare WARP**로 한 번 더 익명화함. 누구에게도 감시받거나 통제받지 않고, 그냥 인터넷을 쓰기 위해서.
+
+> 📎 **참고** — [*How I Hid My VPN*](https://youtu.be/v7Pal3AQyVM)
+
+---
+
+## 2. 전체 아키텍처
+
+```mermaid
+flowchart LR
+ subgraph KR["🇰🇷 한국 · KT 회선 (DPI 노출 구간)"]
+ C["클라이언트<br/>sing-box · TUN<br/>uTLS = chrome"]
+ end
+ subgraph JP["🇯🇵 AWS 도쿄 · ap-northeast-1"]
+ X["Xray 서버<br/>VLESS + Reality :443<br/>위장 SNI = www.example.jp"]
+ end
+ subgraph WP["☁️ Cloudflare WARP"]
+ W["WARP 엣지 · NRT<br/>AS13335<br/>출구 IP 104.28.243.105"]
+ end
+ NET(("🌐 인터넷<br/>일반 목적지"))
+
+ C ==>|"① Reality TLS 1.3 핸드셰이크<br/>DPI엔 'example.jp 보는 크롬'으로 보임"| X
+ X ==>|"② WireGuard / WARP<br/>(기본 경로)"| W ==> NET
+
+ classDef censored stroke:#e11d48,stroke-width:3px;
+ class C,X censored;
+```
+
+**핵심 설계 2가지:**
+
+1. **검열받는 구간(클라이언트 ↔ 도쿄 서버)** 은 Reality로 감싸 *유명 일본 사이트로 가는 정상 TLS 1.3*으로 위장한다.
+2. **검열받지 않는 구간(도쿄 서버 ↔ Cloudflare)** 에서만 WireGuard(WARP)를 쓴다. → WireGuard의 지문 문제를 검열망에 노출하지 않고 "출구 IP 세탁" 이득만 취한다.
+
+> **왜 WARP를 뒤에 두나?** 최종 목적지 서버가 보는 IP가 **AWS(AS16509)** 가 아니라 **Cloudflare(AS13335)** 가 됨. AWS 대역은 "데이터센터 = 봇/프록시 의심"으로 차단당하기 쉽지만, WARP 엣지 IP는 수많은 실사용자가 공유하는 소비자 대역이라 평판이 좋음. **내 서버 IP조차 최종 목적지에 노출되지 않는다**는 게 핵심.
+
+---
+
+## 3. 기존 WireGuard VPN의 약점
+
+WireGuard는 빠르고 코드가 작고 암호학적으로 우수하지만, **검열 회피 도구로는 치명적인 약점**들이 있음.
+
+| 약점 | 설명 | DPI가 하는 일 |
+|---|---|---|
+| **고정된 핸드셰이크 지문** | Handshake Initiation이 **정확히 148바이트**, 뒤이어 Responder는 **정확히 92바이트**. 매 패킷 첫 4바이트에 메시지 타입(`0x01` init, `0x02` response …)이 그대로. 난독화가 전혀 없음. | `148바이트 → 92바이트 UDP 흐름` 규칙 하나로 거의 모든 WireGuard 핸드셰이크를 포착 |
+| **UDP 기반** | 많은 검열망/기업망이 443/UDP를 아예 throttle 하거나, QUIC 아닌 미식별 UDP를 drop. | 프로토콜 몰라도 "UDP 대량 장수명" 자체를 차단 |
+| **능동 탐지(active probing)에 취약** | 아무나 WG 핸드셰이크를 던져보면 서버가 반응하거나(응답) 침묵함 → 응답 패턴으로 서버 식별 가능. | 의심 IP:포트에 프로브 패킷 발사 → VPN 서버 확정 후 블랙리스트 |
+| **위장·커버 트래픽 없음** | SNI 위장, 인증서 위장, 정상 트래픽 mimicry 개념 자체가 없음. 포트를 443으로 바꿔도 페이로드가 TLS가 아니라서 바로 티남. | "443인데 TLS가 아님" → 이상치 플래깅 |
+| **키 노출 시 즉시 식별** | 트래픽에 프로토콜 정체성이 그대로 드러나므로, 한 번 지목되면 그 엔드포인트는 영구 차단. | IP 평판 DB에 등재 |
+
+2026년 기준 Sandvine·Allot·Huawei의 상용 DPI 장비는 **WireGuard 탐지 규칙을 기본 탑재**하며, 최신 미들박스는 패킷 100개만으로 타이밍·엔트로피 기반 ML 분류로 WireGuard를 복호화 없이 식별함. 실제 차단 성적표도 참혹함 — **러시아에서 순정 WireGuard 성공률 약 12%, 이란 98% 패킷 손실, 중국 GFW는 수 초 내 포착**, 그 외 터키·벨라루스·미얀마 등 10개국+가 DPI 레벨에서 차단/스로틀 중임.
+
+> 이 때문에 **AmneziaWG** 같은 "난독화 WireGuard" 파생이 등장함. 순정 WireGuard의 고정 패킷 크기에 랜덤 정크·가변 헤더를 덧대 시그니처를 흩뜨리는 방식인데, *"WireGuard는 그대로 쓰면 잡힌다"*는 사실을 업계가 인정했다는 방증임. 이 글의 Reality는 한 발 더 나아가 **"난독화된 무언가"가 아니라 아예 "정상 TLS 그 자체"로** 보이게 함.
+
+**요약**: WireGuard는 *"나는 WireGuard입니다"* 라고 큰 소리로 외치며 접속함. 검열자는 들을 귀만 있으면 됨. 반면 Reality는 *"저는 그냥 example.jp 보는 사람인데요"* 라고 말하고, **그 증거로 진짜 example.jp의 TLS 인증서까지 제시**함(아래 4·5장에서 실증).
+
+---
+
+## 4. 구성요소
+
+### 4.1 VLESS
+
+VLESS는 VMess의 후속으로, **자체 암호화·타임스탬프 인증을 들어냄.** 왜? 어차피 바깥을 TLS 1.3(Reality)로 감쌀 거라 이중 암호화는 CPU 낭비이기 때문. VLESS 헤더는 버전 바이트 · UUID · 명령 · 목적지 주소 정도로 구성된 **25~50바이트짜리 얇은 라우팅 헤더**에 불과함(OpenVPN 100바이트+ 대비). 여기에 `xtls-rprx-vision` flow를 얹어 TLS-in-TLS로 생기는 특유의 레코드 크기 패턴까지 눌러줌.
+
+> 📎 **참고** — VLESS를 "TLS 1.3 최소 래퍼"로 바이트 단위 해부하고, DPI의 3대 탐지 기법(프로토콜 핑거프린팅 · 통계 분석 · 능동 탐지)을 정리한 글: [habr.com/en/articles/990144](https://habr.com/en/articles/990144/). (1·3장)
+
+### 4.2 Xray-core — 서버 엔진
+
+서버에서 실제로 도는 것은 Xray.
+
+```console
+$ xray version
+Xray 26.3.27 (Xray, Penetrates Everything.) — go1.26.1 linux/amd64
+$ ps -o etime,cmd -C xray
+ ELAPSED CMD
+30-13:49:26 /usr/local/bin/xray run -config /usr/local/etc/xray/config.json # 30일 무중단
+```
+
+`nobody` 권한으로 30일째 무중단 가동.
+
+### 4.3 Reality — 이 구조의 심장
+
+기존 TLS 위장(TLS+웹서버, 예: Trojan)의 근본 문제는 **"내가 제시하는 인증서가 진짜가 아니다"** 임. 검열자가 Certificate Transparency 로그를 뒤지거나 인증서 체인을 검증하면 자작 도메인이 들통남.
+
+**Reality의 발상 전환:**
+
+- 내 서버는 인증서를 **직접 발급/제시하지 않음.**
+- 대신 **실존하는 유명 사이트(여기선 `www.example.jp`)를 "빌려옴".**
+- 정당한 클라이언트(올바른 x25519 키 + shortId 보유)가 접속하면 → Xray가 프록시 모드로 동작.
+- **키가 없는 아무나(=검열자의 능동 탐지 프로브)가 접속하면 → Xray가 그 핸드셰이크를 진짜 `www.example.jp:443`으로 그대로 포워딩**하고, 클라이언트는 example.jp의 진짜 인증서를 받음.
+
+이 "정당한 사용자 ↔ 검열 프로브"의 분기를 그림으로 보면:
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant P as 클라이언트 / 검열 프로브
+ participant R as Xray Reality 서버 :443
+ participant N as 진짜 www.example.jp
+ alt 올바른 키 보유 (정당한 사용자)
+ P->>R: ClientHello + x25519 · shortId · UUID 임베드
+ R->>R: 키 검증 성공 → 프록시 모드
+ R-->>P: 위장 TLS 세션 수립 → 프록시 터널 개통
+ else 키 없음 (검열자의 능동 탐지 프로브)
+ P->>R: ClientHello (유효 키 없음)
+ R->>N: 핸드셰이크를 진짜 example.jp으로 그대로 포워딩
+ N-->>P: 진짜 example.jp 인증서 (Verification OK)
+ Note over P: 결론 "그냥 example.jp 서버네" → 탐지 실패
+ end
+```
+
+즉 검열자가 아무리 찔러봐도 **"여긴 그냥 example.jp 서버네"** 라는 결론밖에 못 냄.
+
+> 📎 **참고 (Reality 원리)** — 공식 구현 [XTLS/REALITY](https://github.com/XTLS/REALITY), SNI fallback 위장 문서 [Project X — Fallbacks with SNI](https://xtls.github.io/en/document/level-1/fallbacks-with-sni.html), 소스 레벨 분석 [How REALITY works (ObjShadow)](https://objshadow.pages.dev/en/posts/how-reality-works/). 능동 탐지 방어 논의는 [Xray-core Discussion #2623](https://github.com/XTLS/Xray-core/discussions/2623).
+
+**서버 inbound 설정:**
+
+```jsonc
+{
+ "tag": "vless-reality-in",
+ "listen": "0.0.0.0", "port": 443,
+ "protocol": "vless",
+ "settings": {
+ "clients": [{ "id": "<클라이언트-UUID>", "flow": "xtls-rprx-vision" }],
+ "decryption": "none"
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "security": "reality",
+ "realitySettings": {
+ "dest": "www.example.jp:443", // 위장 대상(진짜로 여기로 포워딩)
+ "serverNames": ["www.example.jp"], // 허용 SNI
+ "privateKey": "<xray-x25519-개인키>", // 절대 공개 금지
+ "shortIds": ["", "0123456789abcdef"]
+ }
+ },
+ "sniffing": { "enabled": true, "destOverride": ["http","tls","quic"], "routeOnly": true }
+}
+```
+
+> **위장 대상(dest) 선정 기준** — 아무 사이트나 되는 게 아님. ① TLS 1.3 + X25519를 지원하고, ② 서버와 **같은 클라우드/ASN/리전**에 있어야 자연스러움. 도쿄 AWS 서버라면 **AWS에 호스팅된(→인증서 발급자가 `Amazon`으로 잡히는) 일본 대형 사이트**를 고르면 위장이 한층 자연스러움(서버와 같은 클라우드·ASN). 이 글에서는 위장 도메인을 문서용 예시 `www.example.jp`로 표기함. 실제로 KT DPI가 프록시 IP를 플래깅했을 때, SNI를 **서버와 같은 리전·ASN의 호스트**로 맞추는 것이 안정성에 결정적.
+
+### 4.4 WARP outbound — 출구 세탁
+
+서버의 기본 출구는 `direct`(AWS IP 노출)가 아니라 **Cloudflare WARP(userspace WireGuard)**.
+
+```jsonc
+{
+ "tag": "warp-out",
+ "protocol": "wireguard",
+ "settings": {
+ "secretKey": "<WARP-개인키>",
+ "address": ["172.16.0.2/32", "2606:4700:110:84e7:...../128"],
+ "peers": [{
+ "publicKey": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
+ "endpoint": "engage.cloudflareclient.com:2408",
+ "allowedIPs": ["0.0.0.0/0", "::/0"],
+ "keepAlive": 25
+ }],
+ "mtu": 1280
+ }
+}
+```
+
+여기서 중요한 점: **이 WireGuard는 Xray 내부의 userspace 구현**이라 커널 인터페이스(`wg show`)로 안 보이고, **AWS↔Cloudflare 구간(검열 없는 해외 구간)에서만** 흐름. 즉 WireGuard의 지문 문제를 한국 검열망에 절대 노출하지 않으면서 "출구 IP = Cloudflare"라는 이득만 챙김.
+
+### 4.5 라우팅 — 하드닝
+
+```jsonc
+"routing": {
+ "rules": [
+ { "ip": ["geoip:private"], "outboundTag": "block" }, // SSRF/내부망 스캔 차단
+ { "protocol": ["bittorrent"], "outboundTag": "block" }, // 남용 방지(IP 평판 보호)
+ { "ip": ["203.0.113.10/32"], "port": "22", "outboundTag": "loopback-ssh" }, // 관리용
+ { "ip": ["203.0.113.10/32"], "port": "8080", "outboundTag": "loopback-speedtest" },
+ { "network": "tcp,udp", "outboundTag": "warp-out" } // 나머지 전부 → WARP
+ ]
+}
+```
+
+- **`geoip:private` → block**: 프록시를 통해 내부망을 스캔하는 SSRF류를 원천 차단.
+- **bittorrent → block**: 토렌트로 인한 abuse 신고가 WARP/AWS IP 평판을 죽이는 것을 방지.
+- **loopback-ssh**: 별도 포트를 여는 대신, 프록시 터널 안으로 서버 자신의 22에 접근(관리 트래픽도 위장된 채로).
+
+### 4.6 클라이언트 — sing-box (TUN + FakeIP)
+
+클라이언트는 sing-box를 TUN 모드로 돌려 **시스템 전체 트래픽**을 투명하게 삼킴.
+
+```jsonc
+{
+ "outbounds": [{
+ "type": "vless",
+ "server": "203.0.113.10", "server_port": 443,
+ "uuid": "<클라이언트-UUID>",
+ "flow": "xtls-rprx-vision",
+ "tls": {
+ "enabled": true,
+ "server_name": "www.example.jp",
+ "reality": { "enabled": true,
+ "public_key": "<서버-Reality-공개키>",
+ "short_id": "0123456789abcdef" },
+ "utls": { "enabled": true, "fingerprint": "chrome" } // ← Chrome처럼 위장
+ }
+ }],
+ "dns": { "servers": [
+ { "tag": "proxy-dns", "type": "https", "server": "1.1.1.1", "detour": "proxy" }, // DoH를 프록시 너머에서
+ { "tag": "fakeip", "type": "fakeip", "inet4_range": "198.18.0.0/15" } // DNS 유출 방지
+ ]}
+}
+```
+
+두 가지가 특히 중요함.
+
+- **`utls: chrome`** — TLS ClientHello를 진짜 크롬 브라우저의 지문과 동일하게 생성함(뒤에서 패킷으로 증명). uTLS는 [refraction-networking/utls](https://github.com/refraction-networking/utls) 라이브러리로, Go 표준 TLS의 티나는 지문 대신 특정 브라우저 지문을 흉내 냄.
+- **FakeIP + DoH-over-proxy** — 도메인을 로컬에서 절대 해석하지 않음. `198.18.x` 가짜 IP를 즉시 반환하고 실제 해석은 프록시 건너편에서 일어나므로 **DNS 유출이 0**임.
+
+---
+
+## 5. PoC — 패킷 단위로 증명
+
+**실제 커맨드와 실제 응답**. 검열자의 세 가지 관점(① 출구 IP 추적, ② 능동 탐지, ③ 수동 패킷 관찰)에서 각각 뚫리는지 검증.
+
+### PoC-1. 출구 IP 세탁 확인
+
+```console
+$ curl -s https://api.ipify.org
+104.28.243.105
+
+$ curl -s https://www.cloudflare.com/cdn-cgi/trace | grep -E '^(ip|warp|colo|loc)='
+ip=2a09:bac5:4305:dc::16:175
+colo=NRT
+loc=JP
+warp=on
+
+$ curl -s "http://ip-api.com/line/?fields=query,country,city,isp,as"
+104.28.243.105
+Japan
+Tokyo
+Cloudflare, Inc.
+AS13335 Cloudflare, Inc.
+```
+
+**해석:**
+
+| 관점 | 실제 값 | 목적지가 보는 값 |
+|---|---|---|
+| 내 진짜 위치 | 한국, KT | ❌ 안 보임 |
+| 프록시 서버 | `203.0.113.10` (AWS, **AS16509**, 도쿄) | ❌ 안 보임 |
+| **목적지가 보는 출구** | — | ✅ `104.28.243.105` (**Cloudflare AS13335**, 도쿄) |
+
+`warp=on`이 WARP 경유를 확정함. 3단 홉(한국→프록시 서버→WARP)을 거쳤는데 최종 목적지는 **Cloudflare 소비자 대역**만 봄. **프록시 서버 IP 노출되지 않음.**
+
+### PoC-2. 능동 탐지 저항 — "찔러봐도 그냥 example.jp"
+
+검열자가 의심 IP `203.0.113.10:443`에 직접 붙어본다고 가정함. 자격증명이 없는 openssl로 접속:
+
+```console
+$ echo | openssl s_client -connect 203.0.113.10:443 -servername www.example.jp 2>/dev/null \
+ | openssl x509 -noout -subject -issuer -dates
+subject=CN=www.example.jp
+issuer=C=US, O=Amazon, CN=Amazon RSA 2048 M01
+notBefore=Jan 19 00:00:00 2026 GMT
+notAfter=Jan 14 23:59:59 2027 GMT
+
+# 비교: 진짜 www.example.jp 직접 접속
+$ echo | openssl s_client -connect www.example.jp:443 -servername www.example.jp 2>/dev/null \
+ | openssl x509 -noout -subject -issuer
+subject=CN=www.example.jp
+issuer=C=US, O=Amazon, CN=Amazon RSA 2048 M01
+
+# TLS 파라미터
+$ echo | openssl s_client -connect 203.0.113.10:443 -servername www.example.jp 2>/dev/null \
+ | grep -E 'Protocol|Cipher|Verification'
+Verification: OK
+New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
+Protocol: TLSv1.3
+```
+
+**해석: 완벽하게 동일.**
+
+- 프록시 서버가 돌려준 인증서: `CN=www.example.jp`, 발급자 `Amazon RSA 2048 M01`.
+- 진짜 example.jp가 돌려주는 인증서: **완전히 같음.**
+- `Verification: OK` — 자작 인증서였다면 체인 검증에 실패했겠지만, **진짜 example.jp의 진짜 인증서**이므로 검증을 통과함.
+
+검열자가 이 IP를 아무리 능동 탐지해도 얻는 결론은 **"여기는 example.jp 서버다"** 뿐임. WireGuard 서버였다면 프로브 한 번에 정체가 드러났을 것.
+
+### PoC-3. 수동 관찰 — DPI가 와이어에서 실제로 읽는 바이트
+
+이번엔 검열자가 회선 중간에서 패킷을 그냥 훔쳐본다고 가정함. 클라이언트의 물리 NIC(`eth0`)에서 서버로 나가는 트래픽을 `tshark`로 캡처하고, 그동안 여러 사이트에 curl을 날려 **새 업스트림 연결(=새 Reality 핸드셰이크)** 을 유발함.
+
+```console
+$ tshark -i eth0 -f "host 203.0.113.10 and tcp port 443" -w reality.pcap &
+$ for h in example.com www.wikipedia.org api.ipify.org neverssl.com; do
+ curl -s -o /dev/null "https://$h/?cachebust=$RANDOM"; done
+```
+
+**(3-a) DPI가 보는 SNI와 TLS 버전:**
+
+```console
+$ tshark -r reality.pcap -Y "tls.handshake.type==1" \
+ -T fields -e frame.number -e ip.dst -e tcp.dstport \
+ -e tls.handshake.extensions_server_name -e tls.handshake.ja3_full
+```
+
+| frame | ip.dst | port | SNI | 확장(extension) 순서 |
+|---|---|---|---|---|
+| 31 | 203.0.113.10 | 443 | **www.example.jp** | `0-45-17613-16-11-35-18-5-27-51-13-23-10-65037-65281-43` |
+| 73 | 203.0.113.10 | 443 | **www.example.jp** | `18-35-65281-16-11-17613-65037-5-13-43-51-45-10-23-0-27` |
+| 118 | 203.0.113.10 | 443 | **www.example.jp** | `16-23-0-51-13-10-18-5-11-27-17613-45-65037-43-35-65281` |
+| 186 | 203.0.113.10 | 443 | **www.example.jp** | `65037-23-43-27-16-51-45-18-5-13-35-10-0-65281-11-17613` |
+
+관찰 두 가지:
+
+1. **SNI가 전부 `www.example.jp`** — DPI가 평문으로 읽어낼 수 있는 유일한 목적지 힌트가 "example.jp"임. 실제로 나는 example.com·wikipedia·ipify로 갔지만, **와이어 위에는 그 흔적이 없음.**
+2. **확장 순서가 매 연결마다 셔플됨** — 이건 우연이 아니라 **Chrome 110+ 의 실제 동작(extension 순서 랜덤화)** 을 uTLS가 그대로 흉내낸 것임. 시작 cipher suite도 `4865-4866-4867`(TLS 1.3 AEAD 3종)로 크롬과 동일. 즉 JA3 지문이 연결마다 바뀌어 **"고정 지문 매칭"으로는 절대 못 잡음.** 확장 목록에는 `65037`(GREASE ECH), `17613`(ALPS), `27`(compress_certificate) 등 크롬 고유 확장까지 다 들어있음.
+
+**(3-b) 와이어 바이트 — SNI 호스트명이 평문으로 박힘:**
+
+```console
+# ClientHello 안 SNI 확장의 호스트명 바이트 (frame 31)
+0e 77 77 77 2e 65 78 61 6d 70 6c 65 2e 6a 70 -> "www.example.jp" (0e = 길이 14)
+```
+
+길이 바이트 `0e`(14) 다음에 `77 77 77 2e 65 78 61 6d 70 6c 65 2e 6a 70` = **"www.example.jp"** 가 그대로 평문으로 실림. **이것이 DPI가 회선에서 읽는 전부** — 정상적인 크롬 사용자가 example.jp를 여는 것과 바이트 수준에서 동일함.
+
+### PoC-4. 성능
+
+3단 홉(한국→도쿄 Reality→Cloudflare WARP)을 거치는데도 실사용 성능은 준수함.
+
+```console
+$ for i in 1 2 3; do curl -s -o /dev/null \
+ -w "ttfb=%{time_starttransfer}s total=%{time_total}s\n" \
+ https://www.cloudflare.com/cdn-cgi/trace; done
+ttfb=0.234s total=0.235s
+ttfb=0.258s total=0.259s
+ttfb=0.258s total=0.258s
+
+$ curl -s -o /dev/null -w "speed=%{speed_download} bytes/s\n" \
+ "https://speed.cloudflare.com/__down?bytes=10000000"
+speed=7615746 bytes/s # ≈ 61 Mbps (10MB in 1.31s)
+```
+
+- **TTFB ≈ 235–260 ms** (한국↔도쿄 왕복 + WARP 홉 포함). 브라우징 체감 지장 없음.
+- **다운로드 ≈ 61 Mbps** (단일 스트림, 3홉). VLESS가 이중 암호화를 걷어내고 `xtls-rprx-vision`으로 오버헤드를 줄인 덕.
+
+---
+
+## 6. 전체 여정 (엔드-투-엔드)
+
+`curl https://example.com` 을 쳤을 때 실제로 일어나는 일을 시퀀스로 보면:
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant A as curl / 앱
+ participant S as sing-box (TUN)
+ participant X as Xray 도쿄 :443
+ participant W as WARP / Cloudflare
+ participant D as example.com
+
+ A->>S: DNS 조회 example.com
+ S-->>A: FakeIP 198.18.x.x (실제 해석 안 함 → 유출 0)
+ A->>S: TCP → 198.18.x.x:443 (TUN이 흡수)
+ Note over S: 라우팅 판정 = proxy (전량 프록시)
+ S->>X: Reality 핸드셰이크<br/>SNI=www.example.jp · uTLS=chrome
+ Note over S,X: ⚠ DPI가 보는 유일한 장면<br/>'example.jp 보는 크롬 사용자'
+ S->>X: VLESS 헤더: 진짜 목적지 = example.com:443
+ X->>W: warp-out (Xray 내부 WireGuard)
+ W->>D: 출구 104.28.243.105 로 접속
+ D-->>W: 응답
+ W-->>X: 응답
+ X-->>S: 응답
+ S-->>A: 응답 (curl 완료)
+```
+
+단계별 상세:
+
+1. **DNS** — sing-box가 `example.com`을 가로채 **FakeIP `198.18.x.x`** 를 즉시 반환(실제 해석 안 함 → 유출 0).
+2. **TUN 흡수** — 목적지 `198.18.x.x`로 향하는 패킷을 `singbox-tun`이 삼킴.
+3. **라우팅 판정** — `final: proxy`, 전량 프록시로 보냄.
+4. **Reality 핸드셰이크** — sing-box가 서버 `203.0.113.10:443`에 접속. **uTLS로 크롬 지문의 ClientHello 생성**, SNI=`www.example.jp`. x25519 키교환에 shortId·UUID를 실어 서버에 "정당한 클라이언트"임을 증명. ← **DPI는 여기서 example.jp 접속으로만 봄.**
+5. **VLESS 라우팅** — 터널 안에서 "진짜 목적지는 example.com:443"을 서버에 전달.
+6. **서버 라우팅** — Xray가 `warp-out` 선택.
+7. **WARP 송출** — Xray 내부 WireGuard가 Cloudflare 엣지(NRT)로 패킷 전달.
+8. **최종 출구** — Cloudflare가 `104.28.243.105`로 example.com에 접속. **origin은 Cloudflare IP만 봄.**
+9. **역경로** — 응답이 8→1 역순으로 돌아와 curl에 도착.
+
+검열자가 보는 것: **4번 한 장면뿐**, 그것도 "example.jp 보는 크롬 사용자".
+
+---
+
+## 7. 한계와 운영상 고려사항
+
+해당 구조의 약점과 보완할점.
+
+- **트래픽 상관분석(correlation)**: 검열자가 회선 양단을 다 본다면 타이밍/볼륨 상관으로 추론 여지는 남음. Reality는 콘텐츠·목적지를 가릴 뿐, 메타데이터 상관까지 막지는 못함.
+- **위장 도메인 리스크**: `dest`로 고른 사이트가 죽거나 TLS 스택을 바꾸면 위장이 어색해짐. 서버와 같은 ASN/리전 유지가 중요.
+- **IP 평판**: WARP 출구는 공유 IP라 일부 사이트가 WARP 대역을 차단/캡차함. 그래서 토렌트를 block해 IP 평판을 관리함.
+- **능동 프로브의 정교화**: 검열자가 "정당한 example.jp 사용자라면 하지 않을 행동"(예: 접속 후 HTTP 요청을 안 보냄)을 통계적으로 볼 여지. Reality는 이를 상당히 방어하지만 이론적 표면은 존재.
+- **단일 실패점**: 도쿄 서버 하나. 다중 서버 + 자동 페일오버는 향후 과제.
+
+그럼에도, **평범한 WireGuard/OpenVPN 대비 검열 회피 관점에서 압도적**임. WireGuard가 "첫 패킷에 정체가 드러나는" 반면, 이 구조는 **패킷·인증서·핑거프린트 어느 층위에서도 정상 트래픽과 구분되지 않음**을 위 PoC로 실증함.
+
+---
+
+## 8. 결론
+
+> **검열 회피의 본질은 암호화가 아니라 "구분 불가능성(indistinguishability)"임.**
+
+WireGuard는 트래픽을 완벽히 암호화하지만, *암호화됐다는 사실 자체*를 숨기지 못해 잡힘. Reality는 반대로, 내 트래픽을 **검열자가 절대 차단할 수 없는 무언가(유명 사이트로 가는 평범한 TLS 1.3)로 만듦**으로써 살아남음. 여기에 WARP를 얹어 출구 IP까지 세탁함.
+
+- **출구 IP** → Cloudflare(내 서버 IP조차 은닉) ✅
+- **능동 탐지** → 진짜 example.jp 인증서로 응답 ✅
+- **수동 관찰** → 평문 SNI가 `www.example.jp`, 크롬 uTLS 지문(연결마다 셔플) ✅
+- **성능** → TTFB 250ms, 61Mbps ✅
+
+DPI와의 싸움은 결국 *"내 트래픽이 검열자가 차마 못 끊을 정상 트래픽과 얼마나 똑같아 보이느냐"* 의 게임이고, 이 구조는 그 게임에서 이기도록 설계됨.
+
+---
+
+## 참고자료 (References)
+
+**개념**
+- 🎬 [*How I Hid My VPN* (YouTube)](https://youtu.be/v7Pal3AQyVM) — "암호화가 아니라 은닉" 관점의 개념 소개
+- 📄 [VLESS 심층 해부 (Habr #990144)](https://habr.com/en/articles/990144/) — VLESS를 TLS 최소 래퍼로 분석, DPI 3대 탐지 기법
+
+**Reality / XTLS**
+- [XTLS/REALITY (공식 저장소)](https://github.com/XTLS/REALITY)
+- [Project X — Fallbacks with SNI (공식 문서)](https://xtls.github.io/en/document/level-1/fallbacks-with-sni.html)
+- [How REALITY works — 소스 레벨 분석 (ObjShadow)](https://objshadow.pages.dev/en/posts/how-reality-works/)
+- [능동 탐지 방어 논의 — Xray-core Discussion #2623](https://github.com/XTLS/Xray-core/discussions/2623)
+- [VLESS + Reality + XTLS Vision 심화 (Hakim Mhioul)](https://www.mhioul.com/blog/vless-reality-xtls-deep-dive)
+
+**클라이언트 / 라이브러리 / 출구**
+- [sing-box 공식 문서](https://sing-box.sagernet.org)
+- [uTLS — refraction-networking/utls](https://github.com/refraction-networking/utls) (브라우저 TLS 지문 위장)
+- [Cloudflare WARP 클라이언트 문서](https://developers.cloudflare.com/warp-client/)
+
+**WireGuard의 탐지 가능성 (대조군)**
+- [The Imitation Game — WireGuard를 DPI에 안 보이게 만들기 (NT Kernel)](https://www.ntkernel.com/the-imitation-game-how-modern-solutions-make-wireguard-invisible-to-dpi/)
+- [VLESS vs WireGuard/Shadowsocks/Trojan — DPI·속도 비교 (Proxy Poland)](https://proxypoland.com/blog/vless-vs-wireguard-shadowsocks-trojan-comparison)
+- [AmneziaWG 2.0 — 난독화 WireGuard 자가호스팅 (DEV)](https://dev.to/bivlked/amneziawg-20-self-host-an-obfuscated-wireguard-vpn-that-bypasses-dpi-4692)
+- [DPI가 VPN을 탐지·차단하는 원리 (CrazyPrivacy)](https://www.crazyprivacy.com/2025/10/27/understanding-deep-packet-inspection-dpi-how-isps-detect-and-block-vpn-traffic/)
+
+---
diff --git a/content/writing/인지부조화이론.md b/content/writing/인지부조화이론.md
@@ -0,0 +1,123 @@
+---
+title: "인지부조화 이론"
+date: 2026-07-05
+tags: []
+---
+
+
+
+
+
+가면갈 수록 인지부조화가 잔뜩온 사람과 부딪힐일이 많아져 그들을 최대한 이해해보려고 알아봤다.
+이해하기 위해 교보문고에서 "인지부조화 이론 | 레온 페스팅거" 책을 찾을 수 있었고 22,000원이라는 거금의 투자를 하였다.
+내 머리 스텟이 딸려서 그런지 번역이 똥인지는 모르겠지만 책을 읽고도 이해가 되지 않는 부분은 Gemini의 도움을 받았으며 그동안 읽었던 책들을 바탕으로 최대한 이해해 보았다.
+
+물론 인지부조화, 자기객관화, 가면증후군인 사람들을 "이해"하려고 노력한다는거지 그들의 변명이나 자기 합리화에는 동의할 생각은 절대 없다.
+가면을 여러개 쓰고 때에따라 가면을 바꾸는 사람들은 보통 그걸 사회적 능력이라고 포장하고 자기합리화를 하며 주변인들에게 전파까지 하면서 그런식으로 처절하게 처신했던 노력과 지난세월을 지켜내는데
+일말의 동정심도 없을 뿐더러 필자와 같이 가면을 쓰지 않고도 충분히 사회생활에 녹아들고 문제가 없는 케이스가 있을 수록 가면을 써야 사회에서 생존할 수 있다, 성숙한거다 라는 그들의 주장과 논리가 힘이 떨어질 수 밖에 없고 당사자에게는 전혀 설득과 공감이 되지 않는다.
+
+각설하고 내가 알아본다는것은 인지부조화, 자기객관화, 가면증후군인 사람들의 처절한 몸부림, 왜 그렇게 까지 밖에 할수 없는지, 할수 없었는지를 알아본다는 것임을 분명히 말한다.
+
+필자는 인격을 여러개 만들고 관리하고 유지하기 위해 체력을 기르는 노력을 하기보다 본인을 관리하고 여유가 있으면 본인 주변을 도와주는데에 더욱 힘을 쓸것이고 쓰고 있다.
+결국 인격을 여러개 만들고 관리하기 위해서는 상당량의 정신적 스트레스와 체력이 소모되므로 체력이 뒷받침 되지 않으면 그 가면이 벗겨지는건 시간 문제이다. 물론 가면이 벗겨졌을때의 부작용은 가면이 없이 생활했을때와 비교하면 배로 찾아온다.
+
+# 개요
+미국의 심리학자 레온 페스팅거(Leon Festinger)가 처음 제안한 이 개념은, 개인이 가진 두 가지 이상의 믿음, 가치관, 행동이 서로 모순될 때 발생하는 극심한 심리적 불편함을 뜻함.
+과거에는 특정 윤리적 딜레마 상황에서 주로 발생했다면, 현대인들은 일상적인 삶의 구조 자체가 인지부조화를 유발하도록 설계되어 있음.
+
+# 인지부조화의 원인과 가속되는 이유
+
+## SNS와 전시되는 자아의 충돌(초연결 사회의 역설)
+인스타그램 같은 SNS는 인지부조화를 양산하는 거대한 공장과 같음.
+과거의 사람들은 자신의 실제 삶과 보여지는 삶의 격차가 크지 않았음. 그러나 현재는 철저히 편집되고 보정된 "온라인 속의 완벽한 나"와, 피로하고 불완전한 "현실의 나"라는 두 개의 자아를 동시에 감당해야 함.
+- 신념과 현실의 괴리: 겉으로는 화려한 소비와 행복을 전시하지만, 실제 내면은 우울하고 거지같은(현실) 상황이 반복됨. 뇌는 이 거대한 모순을 해결하지 못하고 만성적인 스트레스와 인지부조화 상태에 빠지게 됨.
+
+## 성과주의 사회의 구조적 모순(자아실현이라는 환상)
+현대 자본주의 사회는 단순히 노동을 요구하는 것을 넘어, "네 일을 사랑해라", "열정을 가져라", "진심을 보여라" 라는 감정적 동요까지 강요함.
+단순히 일을 잘하는것(성과)이 경쟁(차별화)에 큰 도움이 되지 않으니 이제는 감정, 인성, 성의라는 감정적 동요까지 강요함.
+- 감정의 강요: 실제로는 생존을 위해 억지로 일하고 있으면서도, 겉으로는 회사에 헌신하고 자아실현을 하는 척을 해야함. "나는 이 일이 싫다"는 내면의 진실과 "나는 프로페셔널하게 이 일에 몰두하고 있다"는 외부의 행동이 매일 정면으로 충돌하며 심각한 인지부조화를 낳음. 철학자 "한병철"의 "피로사회"에서 말했듯, 스스로를 착취하면서도 그것을 자아실현이라 믿어야 하는 모순이 현대인을 병들게 함.
+
+## 익명성과 다중 페르소나의 요구(급격한 도시화)
+급격한 도시화는 사람들을 거대한 익명서의 바다로 밀어 넣음. 과거 전통적인 공동체에서는 "나"라는 개인의 정체성이 비교적 하나로 일관되게 유지되었음.
+- 파편화된 정체성: 하지만 현대의 개인들은 직장에서의 얼굴, SNS에서의 얼굴, 가족 앞에서의 얼굴, 그리고 혼자 있을 때의 얼굴이 모두 다름. 이처럼 파편화된 다중 페르소나를 상황에 맞게 썼다 벗었다 하는 과정에서, "도대체 진짜 나는 누구인가?" 라는 본질적인 인지부조화가 발생하게 됨.
+
+## 정보의 과잉과 가치관의 붕괴
+현대인들은 매일 수천 개의 엇갈리는 정보와 가치관에 노출됨. "환경을 보호해야 한다"는 신념을 가지면서도 당장 편리한 "배달 음식"을 시켜 먹는 행동 사이의 괴리 같은 자잘한 모순들이 일상 곳곳에 산재해 있음.
+너무 많은 정보와 선택지는 역설적으로 우리의 행동과 신념이 일치하기 어렵게 만들고, 이로 인한 작은 인지부조화들이 쌓여 만성적인 피로로 이러짐.
+
+# 가면을 쓰는건 결국 에너지가 고갈되고 스트레스를 동반하는데도 고통을 감수하면서 까지 가면을 쓰면서 인지부조화의 늪으로 밀어넣는 이유
+
+## 즉각적인 도파민 보상과 지연되는 고통
+인스타그램 같은 SNS에서 정성껏 꾸며낸 자아(가면)를 전시하면, "좋아요"라는 확실하고 즉각적인 쾌락(도파민)이 주어짐. 반면, 가짜 자아를 연기하면서 쌓이는 정신적 피로감이나 인지부조화는 서서히 영혼을 갉아먹는 지연되는 고통임.
+인간의 뇌는 먼 미래의 고통보다 눈앞의 강력한 보상에 훨씬 취약함. 결국 피로하다는 것을 이성적으로 알면서도, 그 즉각적인 사회적 인정과 관심의 달콤함에 중독되어 계속해서 가면을 고쳐 쓰게 됨.
+
+## 고립에 대한 원초적인 공포(진화심리학적 관점 Ref. 집단 착각 | 토드 로즈)
+인간은 본질적으로 무리에서 떨어지면 생존할 수 없도록 진화된 사회적 동물임. 현대 사회에서 가면을 벗는 행위는 곧 주류 무리에서 이탈하는 것, 즉 사회적 죽음을 의미하는 것으로 무의식중에 받아들여짐.
+모두가 완벽하고 행복한 가면을 쓰고 있는 SNS나 치열한 직장이라는 공간에서, 나 혼자만 지치고 초라한 진짜 얼굴을 드러내는 것은 엄청난 용기를 넘어 생존의 위협으로 느껴짐. 따라서 체력이 바닥나더라도, 무리에서 도태되고 소외당하지 않기 위해 가면을 쓰는 것.
+
+## 가면의 군비 경쟁과 상대적 박탈감
+현대 사회는 일종의 가면 군비 경쟁 상태에 빠져 있음. 타인의 가면(완벽한 휴가, 성공적인 커리어, 행복한 연애)을 그 사람의 진실이라고 착각하게 되면, 나의 현실은 한없이 초라해 보임.
+이 거대한 상대적 박탈감 속에서 살아남기 위해, 사람들은 타인에게 뒤처지지 않으려고 더 두껍고 화려한 가면을 만들어냄. 본인이 가면을 벗으면 경쟁에서 지는 것 같기 때문에, 모두가 피로해하면서도 아무도 먼저 가면을 벗지 못하는 죄수의 딜레마(Prisoner's Dilemma)에 갇히게 됨.
+
+## 매몰 비용과 가면의 동일시
+오랜 시간 공들여 멋진 페르소나를 구축하고 나면, 그 가면에 쌓인 사회적 평판과 인간관계라는 매몰 비용이 발생하게 됨. 가면을 벗어버리는 순간, 그 가짜 자아를 사랑해 주던 사람들과 쌓아 올린 지위가 모두 무너질 것이라는 두려움이 생김.
+나중에는 진짜 나와 가면 쓴 나 사이의 경계가 희미해지면서, 사람들은 오히려 가면을 쓰고 있을 때만 자신이 가치 있는 존재라고 착각하는 지경에 이름. 가짜 자아에 자존감을 의탁하게 되니 벗고 싶어도 벗을 수가 없음.
+
+결국 사람들이 체력 소모를 감수하면서까지 가면을 쓰는 이유는 그것이 미쳐버린 사회 속에서 개인이 선택할 수 있는 "가장 합리적인 생존 전략"이라고 착각하도록 사회가 강요하고 있기 때문.
+
+# 이 정신나간 가면의 군비 경쟁을 끊기
+
+## 과거와 현재의 가면
+- 과거의 생존형 가면: 전통 사회에서의 가면은 철저히 신분과 체면이었음. 사회나 계급이 강제로 씌워준 가면을 쓰고, 그 틀 안에서 튀지 않고 순응하면 무난하게 생존할 수 있었음. 내가 어떤 사람인지 스스로 증명할 필요가 없었음
+- 현대의 증명형 가면: 반면 현대의 비극은 신분제가 사라진 자리에 "자유"라는 이름으로 각자가 스스로 가면을 빚어내고 입증해야 하는 시대임. "나는 얼마나 특별하고, 성공적이며, 흠결 없는 삶을 살고 있는가?"를 스스로 증명해 내야 하는 거대한 무대에 만인이 강제로 올려졌음.
+
+## 이 미친 가면의 군비 경쟁 끊는 방법
+- 완벽함 대신 취약성의 연대: 최근 완벽한 가면 대신 자신의 우울, 번아웃, 실패를 솔직하게 드러내는 사람들이 오히려 더 강력한 지지와 연대를 얻는 현상이 늘고 있음. "나만 힘든 게 아니었구나"라는 진실된 공감대가 형성되는 순간, 화려한 가면은 그 힘을 읽게 됨.
+- 디지털 디톡스와 JOMO의 부상: 타인의 화련한 가면을 보며 느끼는 FOMO(소외되는 것에 대한 두려움)를 넘어, 자발적으로 연결을 끊고 타인의 시선에서 벗어나려는 JOMO(Joy Of Missing Out)를 추구하는 흐름이 생겨나고 있음.
+- 의도적인 에너지 미니멀리즘: 모든 사람에게 좋은 사람으로 보이기 위한 다중 페르소나를 유지하는 대신, 내 진심을 알아주는 극소수의 관계와 온전한 나의 내면에만 에너지를 집중하는 삶의 방식임.
+
+## 누군가는 해야한다
+이 미친 경쟁을 끝내는 가장 확실한 방법은, 역설적이게도 누군가 먼저 무장 해제를 선언하는 것임.
+모두가 눈치를 보며 가면을 더 두껍게 덧칠할 때, "나는 이 피곤하고 무의미한 게임에서 빠지겠다"며 맨 얼굴을 드러내는 단 한 사람의 용기가, 주변 사람들에게도 덩달아 가면을 벗을 수 있는 엄청난 해방감을 줌.
+
+# 인지부조화 사회를 이용하기(현대 사회의 심리적 결핍이 어떻게 거대한 자본과 산업의 흐름으로 이어지는지)
+이런 인지부조화가 만연한 사회를 구지 바꾸려 노력하지 않고 역으로 이용하는 방법도 있음.
+인지부조화 사회가 지속되고 고도화 될수록 아이돌이나 엔터테인먼트 사업이 더 성장할 수 있음.
+인지부조화가 만연한 사회에서 진심을 드러낼 수 있는 시간과 공간이라는 희소성은 더욱더 가치가 올라가고 인지부조화에 피로감을 느낀 사람들에 대한 공금도 올라갈 것.
+
+## 감정적 해방구(Emotional Sanctuary)의 프리미엄화
+일상생활과 직장에서는 철저하게 감정을 통제하고 페르소나를 연기해야 함. 분노, 슬픔, 맹목적인 열과 같은 날것의 감정을 드러내는 것은 사회적 처벌과 리스크를 동반함.
+하지만 아이돌 공연장이나 팬덤이라는 공간은, 내가 어떤 열정과 환호를 쏟아내도 절대 비난받지 않는 희귀한 감정의 안전지대임.
+일상에서 억눌린 인지부조화의 압력이 커질수록, 사람들은 이 압력을 터뜨릴 수 있는 시공간에 기꺼이 막대한 비용을 지불하게 됨.
+진심을 쏟아낼 공간의 절대적인 희소성이 곧 산업의 폭발적인 성장 동력이 됨.
+
+## 완벽하게 통제된 상처 없는 관계에 대한 갈망
+현실의 인간관계는 상호작용의 연속이며, 필연적으로 오해와 갈등, 감정 소모(모순의 충돌)를 동반함.
+피로감이 극에 달한 현대인들은 더 이상 현실의 복잡한 관계를 맺고 유지할 여력이 부족함.
+반면, 팬과 아이돌의 관계(유사 사회적 관계, Parasocial Interaction)는 철저히 통제되어 있으며 안전함.
+내가 무조건적인 애정을 주더라도 현실적인 갈등을 겪거나 거절당할 확률이 없음.
+내 진심을 100% 투사해도 상처받지 않는다는 이 완벽한 안전장치는, 인지부조화에 지친 사람들에게 가장 매력적인 대체재가 됨.
+
+## 진정성 자체의 고부가가치 상품화
+과거의 엔터테이먼트가 단순히 노래와 춤이라는 퍼포먼스를 팔았다면, 미래의 엔터테인먼트는 철저하게 진정성과 서사를 파는 산업으로 진화할 것.
+가면과 가식이 만연한 세상일수록 사람들은 역설적으로 진짜를 갈망함.
+기획된 무대 위에서 아이돌이 땀 흘리며 보여주는 찰나의 진심, 멤버 간의 끈끈한 유대, 시련을 극복하는 성장 서사 등은 현대인들이 일상에서 찾기 힘든 귀한 감정들임.
+최근 아이돌과 팬이 1:1로 소통하는 듯한 프라이빗 메시지 서비스(버블, 위버스 등)가 거대한 수익을 내는 것도, 이러한 진실된 교감의 감각을 정교하게 상품화했기 때문.
+
+엔터테인먼트 산업은 현대인들의 고갈된 내면을 채워주고 인지부조화의 압력을 낮춰주는 일종의 대체 심리치료나 현대판 종교의 역할까지 흡수하여 그 규모와 영향력을 끝없이 확장해 나갈 것으로 보임.
+다만 여기서 조심해할껀 사람들이 점차 피곤한 현실의 인간관계는 포기하고, 오직 자본을 지불해야만 얻을 수 있는 "엔터테인먼트 속의 안전한 진심"에만 기대게 되면, 우리의 현실 사회는 결국 더욱 파편화되고 고립될 수 있음.
+
+한국인들의 가면 사랑은 여러 미디어에서 손쉽게 볼 수 있다.
+흑백요리사2에서 최강록은 요리실력이 뛰어남에도 불구하고 쭈굴미로 많은 사랑을 받았지만,
+이하성(요리괴물)은 요리실력이 뛰어난거와 별개로 당당한 본인만의 자아를 보여준 탓에 불필요한 악플을 많이 받았다.
+
+사람들은 가식보다 진실성, 진정성, 날것의 모습을 좋아한다고 말한다.
+하지만 그 말에 속으면 안된다.
+사람들은 본인만의 선, 박스 안에서 날것의 모습을 보여주길 원하지 그 이상으로 튀거나 감당되지 않을 정도로 날것의 모습을 보여주면 불편해하고 싫어한다.
+통제되고 기준 안에서 날것의 모습을 보여주길 원하지 절대 말그대로 진짜 날것의 모습을 원하지는 않는다.
+
+대부분의 사람들은 가면속에서 힘겹게 살아간다.
+근데 가면도 없이 편하게 살아가는 사람들을 오면 불편해 하고 시기 질투를 한다.
+동경하는건 일부고 잠깐이다.
+
+대부분의 사람들은 매일 여러개의 가면을 쓰고 힘겹게 살아간다는 점을 명심하자.
\ No newline at end of file
diff --git a/content/writing/퇴사리뷰.md b/content/writing/퇴사리뷰.md
@@ -0,0 +1,97 @@
+---
+title: "퇴사리뷰"
+date: 2026-06-30
+tags: []
+---
+
+이번에는 금방돌아온 퇴사기념 기고문.
+시작해보겠다.
+
+2025년 11월 16일에 AISpera에 합류하게되었다.
+그리고 이 글을 작성하는 2026년 6월 30일 퇴사하였다.
+
+먼저 내가 왜 에이아이스페라에 어떤 목적으로, 어떤 기대로 합류하게 되었는지 부터 알아야 한다.
+
+에이아이스페라에 다니기전 직전 회사는 정보보안 서비스 업체에서 꽤 나름 인지도가 있는 중소기업이었다.
+1세대 정보보안 컨설팅 서비스 업체였으며 오래된 회사연혁을 뒷받침 하듯 오랫동안 관계를 유지하는 굵직한 고객사들이 많았다.
+그래서 고정된 수익이 뒷받침되는 회사였고 뒷받침되는 수익을 바탕으로 대표및 임원진들이 여러 투자를 강행하였지만
+아쉽게도 제대로된 투자로 이어진 적은 없었다.
+또한 기존의 영업방식이 고착화된 나머지 정말 양질의 기술력과 서비스로 다른 경쟁사들과 승부를 보고 사업 수주를 진행하는게 아닌
+단합과 로비로 이루어진 영업방식에 나와 같은 직원들이 이용되고 있었다.
+
+참고로 나에게는 고질적인 병이있다.
+이 병을 고치려고 아무리 발버둥치고 노력하지만 고치기 쉽지 않은 병이 하나있다.
+
+모든것에 진심을 쏟는 병이 있는데 이 병은 나를 살아가게 나아가는 생명이기도 하고 나를 죽이게 하는 죽음과도 같은 병이다.
+
+나는 진심과 진심이 맞붙었을때 살아감을 느낀다.
+진심과 진심은 붙었을때 긍정적인 시너지를 낼 수도 있지만 갈등과 트러블을 유발하기도 한다.
+하지만 그 트러블 조차도 각자의 차이점, 개선 방향성 등을 찾고 유의미한 결과를 얻을 수 있어 약간의 트러블에 비하여 얻을 수 있는 효과가 더 크다고 생각한다.
+그래야 비로소 사람과 사람이 진심으로 부딪혔을때 유기적으로 사고하고 행동하는, 인류 존재의 의의에 걸맞는 유기 화합물, 다세포 동물로써의 목적을 달성하는 것이라고 생각한다.
+
+이것은 사람과 사람이 공존하고 있는 조직과도 같은 맥락으로 이어진다.
+
+조직도 결국 다세포 동물인 사람으로 이루어저 있는 큰 다세포 동물이라고 생각한다.
+임직원 한명한명의 존재가 조직이라는 하나의 큰 동물을 위해 유기적으로 각자가 담당하고 있는 업무를 처리하고 조직이 생존하고 더 나은 방향으로 갈 수 있도록 성장하는 것이 각 임직원들의 존재 의의라고 생각한다.
+결국 이런 큰 유기 생명체를 움직이고 생존하게 하려면 각각의 단세포들이 목적이 있는 역할을 담당해야 한다. 목적이 없는 단세포는 더 이상 기능하지 않고 쓰이지 않으므로 퇴화하고 사라지는게 자연의 섭리이다.
+
+조직도 동일하다.
+조직안에 임직원이라는 각각의 단세포 중 목적이나 존재의 의의가 없는 세포는 죽은 세포와 마찬가지고 그런 세포들이 다수인 조직은 죽은 조직, 죽은 생명과 다름 없다.
+
+다시 본론으로 돌아와서 직전회사는 내눈에 죽은 조직과 다름없었다.
+내가 격어본 대다수의 임직원들은 조직 내에 목적없이 존재하였고 잘 훈련된 개와 원숭이 정도면 충분히 할 수 있는, 인간이 가능한 사고와 고민없이 존재하는 것 뿐이였다.
+이런 죽어가는 조직속에서 내 스스로 매너리즘에 걸리기도 싫었고 최종적으로 내가 만족을 하지 못하였다.
+결국 이직을 결심했고 확율적으로 몸이 가볍고 젊어서 유기적으로 움직이기 쉬운 스타트업이라는 조직의 일원으로서 도전을 하였다.
+
+에이아이스페라 라는 조직에 지원했을때 내 기대는 딱 2가지였다.
+
+1. 몸이 가벼운 만큼 조직의 목표는 "성장"에 초점이 맞춰 있을태니까 모두가 조직의 성장이라는 목표아래에 동일하게 움직이지 않을까?
+2. 몸이 가벼운 만큼 불필요한 프로세스나 시스템을 최소화하여 죽은세포가 없지 않을까?
+
+내가 스타트업이라는 회사규모에 허황된 꿈과 기대치였을 수도 있다.
+하지만 아쉽게도 에이아이스페라에서는 내 기대치와 완전히 어긋났었다.
+
+연혁은 얼마 안되었지만 목적도 의미도없는 불필요한 레거시 정책, 프로세스, 시스템들이 즐비했다.
+누가 어떤 목적으로 만들어졌고 왜 하는지도 아무도 모르고 그냥 "원래부터"라는 단어 아래에 리소스를 좀먹고 있는 불필요한 시스템, 프로세스 들.
+신뢰가 밑바탕 되어있지 않아 불신으로 가득한 마이크로매니징.
+모두가 문제가 있다는걸 인지하고 있지만 이미 암덩어리가 커질대로 커져 암덩어리를 제거하려고 하면 본인과 주변 조직들에 피해가는것을 알고 있어 몇년째 쉬쉬하고 덮기에 급급한 환경.
+그 작은 조직 안에서 기싸움하고 있는 상황.
+
+죽은 조직이 아니라 스스로 자해를 하는 조직을 보았다.
+
+특히 "원래부터"라는 단어 아래에 생존해 왔던 의미없고 불필요한 시스템과 프로세스는 그냥 누군가 한명이 그 굴레를 끊으면 없어진 지도 모르고 조용히 사라지는, 그 누구도 불만이 없고
+존재하는지도 몰랐던 거품들이 모이고 모여 조직의 목을 짓누르고 있었다.
+또한 정말 신기하게 보았던건 스타트업 특성상 대기업 출신 경력자들이 관리자급에 많이 위치할 수 밖에 없는 구조인데 내가 정말 특이하게 보았던건 본인이 다니던 직전회사인 대기업을 그렇게 욕하면서 "전회사는 이렇게 했다" 라는 말로 제 직전회사의 문화와 시스템을 도입하고 구축하려는 모습이 정말 이해를 할 수 없었다.
+절이 싫어 떠난 중이 그 절과 똑같은 시스템과 문화를 도입하여 제2의 절을 만들으려 한다는게 도저히 논리적으로 납득이 불가능 하다는 점에서 신기하게 보였던것 같다.
+
+이번 퇴사를 통해 얻었던 교훈은 유기적인 조직 특성 상 정말 다양한 방식으로 존재할 수 있다는것을 알았고 조직 특성상 자해도 가능하다는 케이스도 확인할 수 있었다.
+8년이라는 생활을 여러 조직안에서 지켜보면서 엇비슷한 조직들을 경험했지만 이번에는 정말 한개도 예상치 못한 새로운 조직경험을 할 수 있었고 다각도에서 다양한 케이스가 있다는것을 확인 시켜준 유의미한 경험이였다.
+
+### 2026년 06월 30일 Update
+
+"문제를 문제라고 인식하는 순간 문제가된다"
+"무지는 죄가 아니다"
+
+이 두가지를 악용하는 사람들이 많다.
+
+긴장이 풀렸을때 언뜻 대화를 해보면 본인들도 잘못되어 가고 있고 문제가 있다는걸 알고 있고 인지하고 있다.
+하지만 그 누구도 조직내에서 실제로 입밖으로 내뱉지 못한다.
+내뱉는 순간 리스크가 너무 크기 때문이다.
+문제가 있다는걸 입 밖으로 내뱉는 순간 문제를 인지했음에도 해결하지 않은 무책임한 낙오자로 찍히기 쉽고
+문제를 인지했음에도 해결하지 못한 무능력한 낙오자로 찍히기 쉽기 때문이다.
+문제를 인지했다는걸 드러나는 순간부터 그 문제에 대한 책임은 오로지 문제를 인지한 사람들 책임이고 문제를 인지하지 않은 사람에게는 무지했다는 이유 하나만으로 면죄권이 부여된다.
+
+이런 환경은 보통 능력없이 입으로 일하는 고인물 조직에서 많이 관찰된다.
+
+이미 그 고인물들이 만든 시스템에서는 아무도 문제제기를 하지 않으니 문제가 없고 정상적으로 잘 돌아가고 있는 조직으로 보인다.
+하지만 그 고인물들이 만든 시스템에 아무것도 모르는 신규직원이 들어와 문제제기를 하면 그 문제를 제기한 신입에게 모든 책임이 떠넘겨 진다.
+그럼 그 신규직원은 혼자 책임을 짊어지는게 생존에 불리하다 판단하여 결국 똑같이 아무도 문제제기를 하지 않는 악순환의 연결고리에 갇히게 된다.
+결국 그 조직은 입발린 소리만 가득한 서로가 서로를 물고늘어지는 우로보로스 마냥 간신배만 남게된다.
+
+사회생활 초년생들은 보통 이런 케이스를 정말 많이 볼탠데 처음에는 그렇게 사회에서 10년 15년 살아남은 능구렁이 같은 고인물들의 "나는 정말몰랐어. 그런게 있었어?" 와 같은 가스라이팅에 넘어가 본인이 너무 예민한가? 나만 문제라 생각하나? 와 같이 딜레마에 빠지기 쉽다.
+하지만 몇년 사회생활을 해보면 그말이 정말 몰라서 했던 말이 아니라 그 사람들의 사회생활 생존법이라는것을 알게되는 때가 있다.
+나는 그걸 깨달았을때 그 가스라이팅에 당했다는 사실에 정말 화가 많이 났었다.
+하지만 외부에는 문제제기를 해도 문제제기한 사람에게 모두 책임을 지는게 아닌 조직 모두가 책임지고 문제를 애써 모른채하고 덮는게 아닌 문제를 당당하게 마주보고 해결해 나가는 건강하고 성숙한 조직들도 있어 위안을 얻었다.
+
+뭣도 아닌 사람이지만 내가 만약 사회초년생에게 딱 한마디만 할 수 있다면 기존 고인물들이 만들어놓은 시스템에 좌절하고 낙담하고 동화되지 말고 그걸 부수고 앞으로 나아가는 사람들도 많으니 절대 그들과 동화되지 말라고 요청하고 싶다.
+한사람 한사람 그 굴레를 벗어야 성숙하고 건강한 사회를 만들 수 있는 원동력이 될 수 있을 거라고 믿어 의심치 않는다.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
@@ -0,0 +1,4226 @@
+{
+ "name": "blog-astro",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "blog-astro",
+ "dependencies": {
+ "astro": "^7.3.3"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.4.1.tgz",
+ "integrity": "sha512-ZYVDW1P58OXyxqZbOMp+/2U9KA7y2esIx9r4pWdpZ/l6fCZhaw4ul+D7EgpRVxYLkyTWqzANbTTUFGPFP8zDHA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@astrojs/compiler-binding-android-arm64": "0.4.1",
+ "@astrojs/compiler-binding-darwin-arm64": "0.4.1",
+ "@astrojs/compiler-binding-darwin-x64": "0.4.1",
+ "@astrojs/compiler-binding-linux-arm64-gnu": "0.4.1",
+ "@astrojs/compiler-binding-linux-arm64-musl": "0.4.1",
+ "@astrojs/compiler-binding-linux-x64-gnu": "0.4.1",
+ "@astrojs/compiler-binding-linux-x64-musl": "0.4.1",
+ "@astrojs/compiler-binding-wasm32-wasi": "0.4.1",
+ "@astrojs/compiler-binding-win32-arm64-msvc": "0.4.1",
+ "@astrojs/compiler-binding-win32-x64-msvc": "0.4.1"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-android-arm64": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.4.1.tgz",
+ "integrity": "sha512-Wtx2DZORuNTLASLLZ8hlrjFaJe1LUloatcCn9EPq7knBhRpjMGXeJANIJMeBZG4igvz7x+3nyAJM2iwCSW9xog==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-darwin-arm64": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.4.1.tgz",
+ "integrity": "sha512-7iwXcU+hB60Y1SvOfnWHNioT72gDsDQKaDipXh2Onwp/TvQMozhbvhy13wJR8Af940a0h6PbddCeJFpj791UDA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-darwin-x64": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.4.1.tgz",
+ "integrity": "sha512-pifjICos49RXhSVKSDu5gDB0/8j0O7DmxSp1DaWW2PyLG5ekwSkcrfOZto5JqhdI7QYMZB3XOiXpsdOXpS4npA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.4.1.tgz",
+ "integrity": "sha512-hsZMlNWc6LqWHh9LlSwTq1/XCY7pxA5rkkKIdZRV8mgbOz9OwLvjEoe5Jry13eIZjMUQO8XnQyzk8o/Pjw2YFA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-arm64-musl": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.4.1.tgz",
+ "integrity": "sha512-a3EW27f+Z9uf57vYmpLgUriA1y9wDhKz57PT8D8XlFZt2HZEWhUxKuxI1jvKVI9p8okYee11WXEm/FHY1G9yoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-x64-gnu": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.4.1.tgz",
+ "integrity": "sha512-XruPTKB/SS4DFzeAjQ1ZyM0ieHDjapHkOSnnzRApZ4FXY2YAI7UY3mETjfJiIS/acyZyrnPH5jObv1gATTdUJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-x64-musl": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.4.1.tgz",
+ "integrity": "sha512-E8j7lGanqNpudtkUbsFaOLJhmwFe4Ecqux7K9hkj4wULIPNZRM2bYZNcMSj8r1oWvaUiwZl8LaaNwv3+14jqhw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-wasm32-wasi": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.4.1.tgz",
+ "integrity": "sha512-vNLxIv41ZE+nmPFtw24cRW5ZXG+ZPcAGtgigtjCP8lvcxKI7u2GrXoMsCgEH5fMw8WCEJpwLvsVBg15rJKAkyA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.2.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.4.1.tgz",
+ "integrity": "sha512-/xTDgRsFDoTh0/+KFXJ74DY4Jqn8CoSHwa8UP5RAblHfnRHkenwfSl4zYDET61O9vXKLk09UzHjjq09yP20/AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-win32-x64-msvc": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.4.1.tgz",
+ "integrity": "sha512-0x5J6iHZO0Fjo/CVzoIbXKzWnGESftDbEUQ/oXQ61HhUAFoR7+T1jfToZSNAS+gYyBXHSTpMWYC7lzarbhClcg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-rs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.4.1.tgz",
+ "integrity": "sha512-//NmAuRhy7eU9iP0ceI5Pymy55M+nUZ//kS6XeOyhBWdDib2KbJOyFaVJ52k7sDkJwTKVox7oPu3SpYiVnGm5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-binding": "0.4.1"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/internal-helpers": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.11.0.tgz",
+ "integrity": "sha512-3rzxJ+xbo0+8YyqOzLziIN32wmsHdCjEVz2sGOpRxJ+Ben/KiLph4ItxBy1abEL+E8fkRzqjg0rfXmaHJGw9JA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.4",
+ "@types/mdast": "^4.0.4",
+ "js-yaml": "^4.3.0",
+ "picomatch": "^4.0.4",
+ "retext-smartypants": "^6.2.0",
+ "shiki": "^4.0.2",
+ "smol-toml": "^1.6.0",
+ "unified": "^11.0.5"
+ }
+ },
+ "node_modules/@astrojs/markdown-satteri": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.4.1.tgz",
+ "integrity": "sha512-EniHbFNa6SQHDih7JnpPos3NWXO6hdt4raBcOosfGmlfc3gP9CI/sESA3VOf1PgJZ7SFfewlZW9Livn0JugEZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.11.0",
+ "@astrojs/prism": "4.0.2",
+ "github-slugger": "^2.0.0",
+ "satteri": "^0.10.3"
+ }
+ },
+ "node_modules/@astrojs/prism": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz",
+ "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==",
+ "license": "MIT",
+ "dependencies": {
+ "prismjs": "^1.30.0"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/telemetry": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz",
+ "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^4.4.0",
+ "dset": "^3.1.4",
+ "is-docker": "^4.0.0",
+ "package-manager-detector": "^1.6.0"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bruits/satteri-darwin-arm64": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.10.5.tgz",
+ "integrity": "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@bruits/satteri-darwin-x64": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.10.5.tgz",
+ "integrity": "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-arm64-gnu": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.10.5.tgz",
+ "integrity": "sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-arm64-musl": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.10.5.tgz",
+ "integrity": "sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-x64-gnu": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.10.5.tgz",
+ "integrity": "sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-x64-musl": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.10.5.tgz",
+ "integrity": "sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.10.5.tgz",
+ "integrity": "sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.2.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-win32-arm64-msvc": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.10.5.tgz",
+ "integrity": "sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@bruits/satteri-win32-x64-msvc": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.10.5.tgz",
+ "integrity": "sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@capsizecss/unpack": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz",
+ "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@clack/core": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.5.1.tgz",
+ "integrity": "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@clack/prompts": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.8.1.tgz",
+ "integrity": "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@clack/core": "1.5.1",
+ "fast-string-width": "^3.0.2",
+ "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz",
+ "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.3",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
+ "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz",
+ "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz",
+ "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz",
+ "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-freebsd-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz",
+ "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz",
+ "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz",
+ "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz",
+ "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz",
+ "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz",
+ "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz",
+ "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz",
+ "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz",
+ "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz",
+ "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz",
+ "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz",
+ "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz",
+ "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz",
+ "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz",
+ "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz",
+ "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz",
+ "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz",
+ "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz",
+ "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
+ "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.11.3"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-webcontainers-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz",
+ "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz",
+ "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz",
+ "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz",
+ "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
+ "license": "MIT"
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz",
+ "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4",
+ "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4"
+ }
+ },
+ "node_modules/@oslojs/encoding": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
+ "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
+ "license": "MIT"
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.150.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.150.0.tgz",
+ "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/oxc-project"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm-eabi": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz",
+ "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz",
+ "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz",
+ "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz",
+ "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz",
+ "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz",
+ "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz",
+ "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz",
+ "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz",
+ "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz",
+ "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz",
+ "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz",
+ "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz",
+ "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz",
+ "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz",
+ "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "license": "MIT"
+ },
+ "node_modules/@shikijs/core": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.3.tgz",
+ "integrity": "sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/primitive": "4.4.3",
+ "@shikijs/types": "4.4.3",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5",
+ "hast-util-to-html": "^9.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.3.tgz",
+ "integrity": "sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.4.3",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.6"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.3.tgz",
+ "integrity": "sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.4.3",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.3.tgz",
+ "integrity": "sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.4.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/primitive": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.3.tgz",
+ "integrity": "sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.4.3",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.3.tgz",
+ "integrity": "sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.4.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.3.tgz",
+ "integrity": "sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
+ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz",
+ "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
+ "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/nlcst": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz",
+ "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz",
+ "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==",
+ "license": "ISC"
+ },
+ "node_modules/am-i-vibing": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz",
+ "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==",
+ "license": "MIT",
+ "dependencies": {
+ "process-ancestry": "^0.1.0"
+ },
+ "bin": {
+ "am-i-vibing": "dist/cli.mjs"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/astro": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-7.3.3.tgz",
+ "integrity": "sha512-NF08hk3edFkVmr9avf9HW/rQyf6NrpbVDyYj2cVN3JfijDhfJYh1ivWVTY5PUd5+rRZn/Vtu4iaeyNlP0Iv6mg==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-rs": "^0.4.0",
+ "@astrojs/internal-helpers": "0.11.0",
+ "@astrojs/markdown-satteri": "0.4.1",
+ "@astrojs/telemetry": "3.3.3",
+ "@capsizecss/unpack": "^4.0.0",
+ "@clack/prompts": "^1.1.0",
+ "@oslojs/encoding": "^1.1.0",
+ "am-i-vibing": "^0.4.0",
+ "aria-query": "^5.3.2",
+ "axobject-query": "^4.1.0",
+ "ci-info": "^4.4.0",
+ "clsx": "^2.1.1",
+ "common-ancestor-path": "^2.0.0",
+ "cookie": "^2.0.1",
+ "devalue": "^5.8.1",
+ "diff": "^9.0.0",
+ "dset": "^3.1.4",
+ "es-module-lexer": "^2.0.0",
+ "esbuild": "^0.28.0",
+ "find-proc": "0.2.0",
+ "flattie": "^1.1.1",
+ "fontace": "~0.4.1",
+ "get-tsconfig": "5.0.0-beta.4",
+ "github-slugger": "^2.0.0",
+ "html-escaper": "3.0.3",
+ "http-cache-semantics": "^4.2.0",
+ "js-yaml": "^4.3.0",
+ "jsonc-parser": "^3.3.1",
+ "magic-string": "^1.0.0",
+ "magicast": "^0.5.2",
+ "mrmime": "^2.0.1",
+ "neotraverse": "^1.0.1",
+ "obug": "^2.1.1",
+ "p-limit": "^7.3.0",
+ "p-queue": "^9.1.0",
+ "package-manager-detector": "^1.6.0",
+ "piccolore": "^0.1.3",
+ "picomatch": "^4.0.4",
+ "shiki": "^4.0.2",
+ "smol-toml": "^1.6.0",
+ "svgo": "^4.0.2",
+ "tinyclip": "^0.1.12",
+ "tinyexec": "^1.0.4",
+ "tinyglobby": "^0.2.15",
+ "ultrahtml": "^1.6.0",
+ "unifont": "~0.7.5",
+ "unstorage": "^1.17.5",
+ "verkit": "^0.4.0",
+ "vite": "^8.0.13",
+ "vitefu": "^1.1.2",
+ "xxhash-wasm": "^1.1.0",
+ "yargs-parser": "^22.0.0",
+ "zod": "^4.5.4"
+ },
+ "bin": {
+ "astro": "bin/astro.mjs"
+ },
+ "engines": {
+ "node": ">=22.12.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/astrodotbuild"
+ },
+ "optionalDependencies": {
+ "sharp": "^0.35.4"
+ },
+ "peerDependencies": {
+ "@astrojs/markdown-remark": "^7.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@astrojs/markdown-remark": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "license": "ISC"
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/commander": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/common-ancestor-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz",
+ "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz",
+ "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cookie-es": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz",
+ "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==",
+ "license": "MIT"
+ },
+ "node_modules/crossws": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
+ "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==",
+ "license": "MIT",
+ "dependencies": {
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz",
+ "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^7.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.2",
+ "nth-check": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+ "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.27.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz",
+ "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/defu": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
+ "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
+ "license": "MIT"
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
+ "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devalue": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz",
+ "integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==",
+ "license": "MIT"
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/diff": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz",
+ "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dset": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
+ "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz",
+ "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==",
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-string-truncated-width": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
+ "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-string-width": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
+ "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-truncated-width": "^3.0.2"
+ }
+ },
+ "node_modules/fast-wrap-ansi": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz",
+ "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-width": "^3.0.2"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/find-proc": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/find-proc/-/find-proc-0.2.0.tgz",
+ "integrity": "sha512-a6h2nTrgB3g5Wrn4au9Ux4kMk7IJtyF6oA8uGn+mk3weLZssUtc1cp6meHIzzuW2o1srrBAxzkmXLbqjRkgB6w==",
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/flattie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
+ "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fontace": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz",
+ "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.2"
+ }
+ },
+ "node_modules/fontkitten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz",
+ "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==",
+ "license": "MIT",
+ "dependencies": {
+ "tiny-inflate": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "5.0.0-beta.4",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz",
+ "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20.20.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/github-slugger": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
+ "license": "ISC"
+ },
+ "node_modules/h3": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz",
+ "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie-es": "^1.2.3",
+ "crossws": "^0.3.5",
+ "defu": "^6.1.6",
+ "destr": "^2.0.5",
+ "iron-webcrypto": "^1.2.1",
+ "node-mock-http": "^1.0.4",
+ "radix3": "^1.1.2",
+ "ufo": "^1.6.3",
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
+ "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+ "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==",
+ "license": "MIT"
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/iron-webcrypto": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
+ "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/brc-dd"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz",
+ "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==",
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "license": "MIT"
+ },
+ "node_modules/lightningcss": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "11.5.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz",
+ "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.6.0"
+ }
+ },
+ "node_modules/magicast": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz",
+ "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.27.1",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+ "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.19",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
+ "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/neotraverse": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz",
+ "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/nlcst-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz",
+ "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/node-fetch-native": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
+ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
+ "license": "MIT"
+ },
+ "node_modules/node-mock-http": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz",
+ "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==",
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz",
+ "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==",
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/ofetch": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz",
+ "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==",
+ "license": "MIT",
+ "dependencies": {
+ "destr": "^2.0.5",
+ "node-fetch-native": "^1.6.7",
+ "ufo": "^1.6.1"
+ }
+ },
+ "node_modules/ohash": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz",
+ "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-parser": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz",
+ "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-to-es": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz",
+ "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==",
+ "license": "MIT",
+ "dependencies": {
+ "oniguruma-parser": "^0.12.2",
+ "regex": "^6.1.0",
+ "regex-recursion": "^6.0.2"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.2.tgz",
+ "integrity": "sha512-Ll0w3fU24vYpXoZmjjZIee6bJQDgG0oAyo1PdmFYI8UDwJJddaHAypxIH9avUu+t+lSsAwKVsb1jDCMIIChliw==",
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "9.3.3",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz",
+ "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^5.0.4",
+ "p-timeout": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.2.tgz",
+ "integrity": "sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-manager-detector": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz",
+ "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==",
+ "license": "MIT"
+ },
+ "node_modules/piccolore": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz",
+ "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==",
+ "license": "ISC"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
+ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.28",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
+ "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.18",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/process-ancestry": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz",
+ "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/radix3": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
+ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==",
+ "license": "MIT"
+ },
+ "node_modules/readdirp": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz",
+ "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
+ "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-recursion": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz",
+ "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-utilities": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
+ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
+ "license": "MIT"
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/retext-smartypants": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz",
+ "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.9.tgz",
+ "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==",
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.150.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm-eabi": "1.2.9",
+ "@rolldown/binding-android-arm64": "1.2.9",
+ "@rolldown/binding-darwin-arm64": "1.2.9",
+ "@rolldown/binding-darwin-x64": "1.2.9",
+ "@rolldown/binding-freebsd-x64": "1.2.9",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.9",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.9",
+ "@rolldown/binding-linux-arm64-musl": "1.2.9",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.9",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.9",
+ "@rolldown/binding-linux-x64-gnu": "1.2.9",
+ "@rolldown/binding-linux-x64-musl": "1.2.9",
+ "@rolldown/binding-openharmony-arm64": "1.2.9",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.9",
+ "@rolldown/binding-win32-x64-msvc": "1.2.9"
+ }
+ },
+ "node_modules/satteri": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.10.5.tgz",
+ "integrity": "sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.5",
+ "@types/hast": "^3.0.5",
+ "@types/mdast": "^4.0.4",
+ "@types/unist": "^3.0.3"
+ },
+ "optionalDependencies": {
+ "@bruits/satteri-darwin-arm64": "0.10.5",
+ "@bruits/satteri-darwin-x64": "0.10.5",
+ "@bruits/satteri-linux-arm64-gnu": "0.10.5",
+ "@bruits/satteri-linux-arm64-musl": "0.10.5",
+ "@bruits/satteri-linux-x64-gnu": "0.10.5",
+ "@bruits/satteri-linux-x64-musl": "0.10.5",
+ "@bruits/satteri-wasm32-wasi": "0.10.5",
+ "@bruits/satteri-win32-arm64-msvc": "0.10.5",
+ "@bruits/satteri-win32-x64-msvc": "0.10.5"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
+ "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz",
+ "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.1.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.8.5"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.35.4",
+ "@img/sharp-darwin-x64": "0.35.4",
+ "@img/sharp-freebsd-wasm32": "0.35.4",
+ "@img/sharp-libvips-darwin-arm64": "1.3.3",
+ "@img/sharp-libvips-darwin-x64": "1.3.3",
+ "@img/sharp-libvips-linux-arm": "1.3.3",
+ "@img/sharp-libvips-linux-arm64": "1.3.3",
+ "@img/sharp-libvips-linux-ppc64": "1.3.3",
+ "@img/sharp-libvips-linux-riscv64": "1.3.3",
+ "@img/sharp-libvips-linux-s390x": "1.3.3",
+ "@img/sharp-libvips-linux-x64": "1.3.3",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.3",
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.3",
+ "@img/sharp-linux-arm": "0.35.4",
+ "@img/sharp-linux-arm64": "0.35.4",
+ "@img/sharp-linux-ppc64": "0.35.4",
+ "@img/sharp-linux-riscv64": "0.35.4",
+ "@img/sharp-linux-s390x": "0.35.4",
+ "@img/sharp-linux-x64": "0.35.4",
+ "@img/sharp-linuxmusl-arm64": "0.35.4",
+ "@img/sharp-linuxmusl-x64": "0.35.4",
+ "@img/sharp-webcontainers-wasm32": "0.35.4",
+ "@img/sharp-win32-arm64": "0.35.4",
+ "@img/sharp-win32-ia32": "0.35.4",
+ "@img/sharp-win32-x64": "0.35.4"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/shiki": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.3.tgz",
+ "integrity": "sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "4.4.3",
+ "@shikijs/engine-javascript": "4.4.3",
+ "@shikijs/engine-oniguruma": "4.4.3",
+ "@shikijs/langs": "4.4.3",
+ "@shikijs/themes": "4.4.3",
+ "@shikijs/types": "4.4.3",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "license": "MIT"
+ },
+ "node_modules/smol-toml": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz",
+ "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/svgo": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz",
+ "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^11.1.0",
+ "css-select": "^6.0.0",
+ "css-tree": "^3.0.1",
+ "css-what": "^7.0.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.1.1",
+ "sax": "1.6.1"
+ },
+ "bin": {
+ "svgo": "bin/svgo.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
+ }
+ },
+ "node_modules/tiny-inflate": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
+ "license": "MIT"
+ },
+ "node_modules/tinyclip": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz",
+ "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==",
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >= 17.3.0"
+ }
+ },
+ "node_modules/tinyexec": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
+ "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/ufo": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz",
+ "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==",
+ "license": "MIT"
+ },
+ "node_modules/ultrahtml": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz",
+ "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==",
+ "license": "MIT"
+ },
+ "node_modules/uncrypto": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
+ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
+ "license": "MIT"
+ },
+ "node_modules/undici": {
+ "version": "8.10.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz",
+ "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unifont": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz",
+ "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "^3.1.0",
+ "ohash": "^2.0.11",
+ "undici": "^8.0.0"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unstorage": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz",
+ "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^3.1.3",
+ "chokidar": "^5.0.0",
+ "destr": "^2.0.5",
+ "h3": "^1.15.10",
+ "lru-cache": "^11.2.7",
+ "node-fetch-native": "^1.6.7",
+ "ofetch": "^1.5.1",
+ "ufo": "^1.6.3"
+ },
+ "peerDependencies": {
+ "@azure/app-configuration": "^1.8.0",
+ "@azure/cosmos": "^4.2.0",
+ "@azure/data-tables": "^13.3.0",
+ "@azure/identity": "^4.6.0",
+ "@azure/keyvault-secrets": "^4.9.0",
+ "@azure/storage-blob": "^12.26.0",
+ "@capacitor/preferences": "^6 || ^7 || ^8",
+ "@deno/kv": ">=0.9.0",
+ "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0",
+ "@planetscale/database": "^1.19.0",
+ "@upstash/redis": "^1.34.3",
+ "@vercel/blob": ">=0.27.1",
+ "@vercel/functions": "^2.2.12 || ^3.0.0",
+ "@vercel/kv": "^1 || ^2 || ^3",
+ "aws4fetch": "^1.0.20",
+ "db0": ">=0.2.1",
+ "idb-keyval": "^6.2.1",
+ "ioredis": "^5.4.2",
+ "uploadthing": "^7.4.4"
+ },
+ "peerDependenciesMeta": {
+ "@azure/app-configuration": {
+ "optional": true
+ },
+ "@azure/cosmos": {
+ "optional": true
+ },
+ "@azure/data-tables": {
+ "optional": true
+ },
+ "@azure/identity": {
+ "optional": true
+ },
+ "@azure/keyvault-secrets": {
+ "optional": true
+ },
+ "@azure/storage-blob": {
+ "optional": true
+ },
+ "@capacitor/preferences": {
+ "optional": true
+ },
+ "@deno/kv": {
+ "optional": true
+ },
+ "@netlify/blobs": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@upstash/redis": {
+ "optional": true
+ },
+ "@vercel/blob": {
+ "optional": true
+ },
+ "@vercel/functions": {
+ "optional": true
+ },
+ "@vercel/kv": {
+ "optional": true
+ },
+ "aws4fetch": {
+ "optional": true
+ },
+ "db0": {
+ "optional": true
+ },
+ "idb-keyval": {
+ "optional": true
+ },
+ "ioredis": {
+ "optional": true
+ },
+ "uploadthing": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/verkit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.4.0.tgz",
+ "integrity": "sha512-sXMwN6DMHeouPfCxkxWkKAmxphWKEenHYY5H1nIBzU3PmDsmJp6kBXJdshjVdpMZuWCmL9SH7KFRx29AylpP6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz",
+ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.33.0",
+ "picomatch": "^4.0.7",
+ "postcss": "^8.5.28",
+ "rolldown": "~1.2.6",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.7.1",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitefu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz",
+ "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==",
+ "license": "MIT",
+ "workspaces": [
+ "tests/deps/*",
+ "tests/projects/*",
+ "tests/projects/workspace/packages/*"
+ ],
+ "peerDependencies": {
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xxhash-wasm": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz",
+ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==",
+ "license": "MIT"
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.6.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
+ "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "blog",
+ "type": "module",
+ "private": true,
+ "engines": {
+ "node": ">=22.12.0"
+ },
+ "scripts": {
+ "dev": "astro dev",
+ "prebuild": "node scripts/check-content.mjs && node scripts/build-info.mjs",
+ "build": "astro build",
+ "preview": "astro preview",
+ "check": "node scripts/check-content.mjs"
+ },
+ "dependencies": {
+ "astro": "^7.3.3"
+ }
+}
diff --git a/public/CNAME b/public/CNAME
@@ -0,0 +1 @@
+blog.sqzer.com
diff --git a/public/uploads/etc-systemd-sysmon-sayonggi-for-linux/image-1.png b/public/uploads/etc-systemd-sysmon-sayonggi-for-linux/image-1.png
Binary files differ.
diff --git a/public/uploads/network-arp/image-10.png b/public/uploads/network-arp/image-10.png
Binary files differ.
diff --git a/public/uploads/network-bind-9-dns-server-seolci/image-11.png b/public/uploads/network-bind-9-dns-server-seolci/image-11.png
Binary files differ.
diff --git a/public/uploads/network-bind-9-dns-server-seolci/image-12.png b/public/uploads/network-bind-9-dns-server-seolci/image-12.png
Binary files differ.
diff --git a/public/uploads/network-bind-9-dns-server-seolci/image-13.png b/public/uploads/network-bind-9-dns-server-seolci/image-13.png
Binary files differ.
diff --git a/public/uploads/network-bind-9-dns-server-seolci/image-14.png b/public/uploads/network-bind-9-dns-server-seolci/image-14.png
Binary files differ.
diff --git a/public/uploads/network-bind-9-dns-server-seolci/image-15.png b/public/uploads/network-bind-9-dns-server-seolci/image-15.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-1.png b/public/uploads/network-security-arp-spoofing/image-1.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-2.png b/public/uploads/network-security-arp-spoofing/image-2.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-3.png b/public/uploads/network-security-arp-spoofing/image-3.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-4.png b/public/uploads/network-security-arp-spoofing/image-4.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-5.png b/public/uploads/network-security-arp-spoofing/image-5.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-6.png b/public/uploads/network-security-arp-spoofing/image-6.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-7.png b/public/uploads/network-security-arp-spoofing/image-7.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-8.png b/public/uploads/network-security-arp-spoofing/image-8.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image-9.png b/public/uploads/network-security-arp-spoofing/image-9.png
Binary files differ.
diff --git a/public/uploads/network-security-arp-spoofing/image.png b/public/uploads/network-security-arp-spoofing/image.png
Binary files differ.
diff --git a/public/uploads/network-security-doh-dns-over-https/image-16.png b/public/uploads/network-security-doh-dns-over-https/image-16.png
Binary files differ.
diff --git a/public/uploads/network-security-doh-dns-over-https/image-17.png b/public/uploads/network-security-doh-dns-over-https/image-17.png
Binary files differ.
diff --git a/public/uploads/research/1-775fc3ed.webp b/public/uploads/research/1-775fc3ed.webp
Binary files differ.
diff --git a/public/uploads/research/2-ee522a4a.webp b/public/uploads/research/2-ee522a4a.webp
Binary files differ.
diff --git a/public/uploads/research/3-5a25cd9b.webp b/public/uploads/research/3-5a25cd9b.webp
Binary files differ.
diff --git a/public/uploads/research/aiodyssey/2026-08-13-165932-d1d8a787.webp b/public/uploads/research/aiodyssey/2026-08-13-165932-d1d8a787.webp
Binary files differ.
diff --git a/public/uploads/research/images-193fd9fb.webp b/public/uploads/research/images-193fd9fb.webp
Binary files differ.
diff --git a/public/uploads/research/img_4305-1c65d268.webp b/public/uploads/research/img_4305-1c65d268.webp
Binary files differ.
diff --git a/public/uploads/research/we_need_to_talk/image-e1aa4196.webp b/public/uploads/research/we_need_to_talk/image-e1aa4196.webp
Binary files differ.
diff --git a/scripts/build-info.mjs b/scripts/build-info.mjs
@@ -0,0 +1,23 @@
+/**
+ * build-info.mjs — 사이트가 자기 출처를 말하게 한다.
+ *
+ * 배포 훅/CI 는 payload 를 돌려주지 않으므로 "배포가 끝났는가"를 플랫폼에 물을 수 없다.
+ * 대신 **라이브 사이트에 "너는 어느 content 커밋으로 지어졌나"** 를 묻는다.
+ */
+import { writeFile, mkdir } from 'node:fs/promises';
+import { execFileSync } from 'node:child_process';
+
+const SRC = process.env.CONTENT_DIR ?? '.';
+const git = (args, cwd) => {
+ try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore','pipe','ignore'] }).trim(); }
+ catch { return null; }
+};
+const info = {
+ contentSha: process.env.CONTENT_SHA ?? git(['rev-parse', 'HEAD'], SRC),
+ contentRef: git(['rev-parse', '--abbrev-ref', 'HEAD'], SRC),
+ appSha: process.env.GITHUB_SHA ?? git(['rev-parse', 'HEAD'], '.'),
+ builtAt: new Date().toISOString(),
+};
+await mkdir('public', { recursive: true });
+await writeFile('public/build-info.json', JSON.stringify(info, null, 1) + '\n');
+console.log(`build-info: content ${String(info.contentSha).slice(0, 8)} / app ${String(info.appSha).slice(0, 8)}`);
diff --git a/scripts/check-content.mjs b/scripts/check-content.mjs
@@ -0,0 +1,73 @@
+/**
+ * check-content.mjs — 배포 전 콘텐츠 게이트.
+ *
+ * CI/CD 가 배포 수단 그 자체가 되면서 이것이 **유일한 안전망**이다.
+ * 자동 테스트 0개였던 이전 시스템의 자리를 대신한다.
+ *
+ * 단계적 엄격도: CONTENT_STRICT 로 어디까지 에러로 볼지 정한다.
+ * urls,schema — 마이그레이션 직후(덱·태그가 아직 비어 있다)
+ * all — 백필 완료 후. **한 번 올리면 다시 내리지 않는다**
+ */
+import { readdir, readFile } from 'node:fs/promises';
+import { existsSync } from 'node:fs';
+import path from 'node:path';
+
+const SRC = process.env.CONTENT_DIR ?? '.';
+const STRICT = new Set((process.env.CONTENT_STRICT ?? 'urls,schema').split(',').map((s) => s.trim()));
+const on = (k) => STRICT.has('all') || STRICT.has(k);
+
+const errors = [], warns = [];
+const err = (f, m) => errors.push(`${f}: ${m}`);
+const warn = (f, m) => warns.push(`${f}: ${m}`);
+const req = (k, f, m) => (on(k) ? err(f, m) : warn(f, m));
+
+const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));
+/** 하위 뷰 이름과 충돌하면 라우트가 깨진다. 연도 세그먼트가 막아 주지만 방어선을 하나 더 둔다. */
+const RESERVED = new Set(['essay', 'research', 'video', 'slides', 'podcast', 'index', 'page', 'tags', 'feed', 'about']);
+
+const files = [];
+{
+ const d = path.join(SRC, 'content', 'writing');
+ if (existsSync(d))
+ for (const f of await readdir(d)) if (f.endsWith('.md') && !f.startsWith('_')) files.push(path.join(d, f));
+}
+if (files.length === 0) err('(전체)', 'content 에서 마크다운을 하나도 못 찾았다 — 체크아웃 실패일 수 있다');
+
+const seen = new Map();
+for (const abs of files) {
+ const rel = path.relative(SRC, abs).split(path.sep).join('/');
+ const raw = await readFile(abs, 'utf8');
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
+ if (!m) { err(rel, 'front matter 가 없다'); continue; }
+ const fm = m[1];
+ const get = (k) => (new RegExp(`^${k}:\s*(.*)$`, 'm').exec(fm)?.[1] ?? '').trim().replace(/^["']|["']$/g, '');
+
+ const title = get('title');
+ const date = get('date');
+ if (!title) err(rel, 'title 이 없다');
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) err(rel, `date 가 YYYY-MM-DD 가 아니다: ${date || '(없음)'}`);
+
+ const slug = asciiLower(path.basename(abs, '.md'));
+ if (RESERVED.has(slug)) err(rel, `예약어와 충돌하는 슬러그: ${slug}`);
+ if (slug !== slug.normalize('NFC')) err(rel, '파일명이 NFC 정규화되지 않았다 (한글 자모 분리)');
+
+ const url = `/writing/${date.slice(0, 4)}/${slug}/`;
+ if (seen.has(url)) err(rel, `URL 충돌: ${url} (이미 ${seen.get(url)})`);
+ else seen.set(url, rel);
+
+ // 덱·태그는 백필 전이므로 단계적으로만 강제한다
+ if (!get('deck')) req('deck', rel, 'deck 이 비어 있다');
+ if (/^tags:\s*\[\s*\]\s*$/m.test(fm)) req('tags', rel, 'tags 가 비어 있다');
+
+ // 본문이 참조하는 이미지가 실제로 있는가
+ for (const im of raw.matchAll(/\/uploads\/([^\s"')\]]+)/g)) {
+ const key = im[1].replace(/[.,)]+$/, '');
+ if (!existsSync(path.join(SRC, 'public', 'uploads', key))) req('uploads', rel, `참조된 이미지가 없다: /uploads/${key}`);
+ }
+}
+
+console.log(`check-content: 문서 ${files.length}개 / URL ${seen.size}개 / STRICT=${[...STRICT].join(',')}`);
+for (const w of warns) console.log(` warn ${w}`);
+for (const e of errors) console.error(` ERROR ${e}`);
+if (errors.length) { console.error(`\n실패 ${errors.length}건 — 빌드를 중단한다.`); process.exit(1); }
+console.log(` 경고 ${warns.length}건 / 에러 0건 — 통과`);
diff --git a/src/content.config.ts b/src/content.config.ts
@@ -0,0 +1,46 @@
+import { defineCollection, z } from 'astro:content';
+import { glob } from 'astro/loaders';
+
+/** Hugo 규칙: URL 세그먼트는 **ASCII 만** 소문자화한다. 한글·키릴은 건드리지 않는다. */
+export const asciiLower = (s: string): string =>
+ s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));
+
+/**
+ * YAML 이 `2025-12-07` 을 Date 로 파싱한다. **로컬 시간으로 읽으면 안 된다** —
+ * UTC 기준으로 뽑아야 코퍼스 날짜가 하루 밀리지 않는다.
+ */
+const dateString = z.union([z.string(), z.date()]).transform((v) =>
+ typeof v === 'string' ? v.slice(0, 10) : v.toISOString().slice(0, 10),
+);
+
+/** `deck:` 을 비우면 YAML 이 null 을 준다. undefined 로 흡수하지 않으면 빌드가 죽는다. */
+const optText = z.preprocess(
+ (v) => (v === null || v === '' ? undefined : v),
+ z.string().optional(),
+);
+
+const slugOf = ({ entry }: { entry: string }) =>
+ asciiLower(entry.replace(/^.*\//, '').replace(/\.md$/, ''));
+
+const writing = defineCollection({
+ loader: glob({ base: './content/writing', pattern: '*.md', generateId: slugOf }),
+ schema: z.object({
+ title: z.string(),
+ /** 한글 원제 보존용. 영문 제목으로 갈아탈 때 원본을 잃지 않는다. */
+ titleKo: optText,
+ date: dateString,
+ /** 인덱스에서 덱이 곧 카드다. 백필 전까지는 비어 있어도 된다. */
+ deck: optText,
+ /** 하위 뷰(Essay/Research)는 경로가 아니라 이 값으로 가른다. */
+ type: z.enum(['essay', 'research']).optional(),
+ tags: z.array(z.string()).default([]),
+ draft: z.boolean().optional(),
+ }),
+});
+
+const about = defineCollection({
+ loader: glob({ base: './content', pattern: 'about.md', generateId: () => 'about' }),
+ schema: z.object({ title: z.string(), date: dateString.optional() }),
+});
+
+export const collections = { writing, about };
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
@@ -0,0 +1,34 @@
+---
+interface Props { title: string; description?: string }
+const { title, description } = Astro.props;
+const NAV = [
+ { label: 'About', href: '/' },
+ { label: 'Papers', href: '/papers/' },
+ { label: 'Talk', href: '/talk/' },
+ { label: 'Code', href: '/code/' },
+ { label: 'Writing', href: '/writing/' },
+];
+const here = Astro.url.pathname;
+---
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <title>{title}</title>
+ {description && <meta name="description" content={description} />}
+ <link rel="canonical" href={new URL(here, Astro.site)} />
+ <link rel="alternate" type="application/rss+xml" title="sqzer" href="/index.xml" />
+ </head>
+ <body>
+ <header>
+ <a href="/">sqzer</a>
+ <nav>
+ {NAV.map((n) => (
+ <a href={n.href} aria-current={here === n.href ? 'page' : undefined}>{n.label}</a>
+ ))}
+ </nav>
+ </header>
+ <main><slot /></main>
+ </body>
+</html>
diff --git a/src/lib/order.ts b/src/lib/order.ts
@@ -0,0 +1,16 @@
+/**
+ * 목록 정렬 — date DESC → Intl.Collator('en') title → path.
+ * 이 순서가 인덱스·피드·sitemap 을 동시에 결정한다. DB/파일시스템 순서에 맡기면 안 된다.
+ */
+const collator = new Intl.Collator('en');
+
+export interface Sortable { data: { date: string; title: string }; id: string }
+
+export function byHugoOrder<T extends Sortable>(a: T, b: T): number {
+ if (a.data.date !== b.data.date) return a.data.date < b.data.date ? 1 : -1;
+ const t = collator.compare(a.data.title, b.data.title);
+ return t !== 0 ? t : (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
+}
+
+/** 글의 정식 경로. 연도는 date 에서 파생된다. */
+export const postUrl = (p: Sortable): string => `/writing/${p.data.date.slice(0, 4)}/${p.id}/`;
diff --git a/src/pages/404.astro b/src/pages/404.astro
@@ -0,0 +1,7 @@
+---
+import Base from '../layouts/Base.astro';
+---
+<Base title="Not found :: sqzer">
+ <h1>404</h1>
+ <p>That page doesn’t exist. <a href="/writing/">See all writing</a>.</p>
+</Base>
diff --git a/src/pages/code/index.astro b/src/pages/code/index.astro
@@ -0,0 +1,7 @@
+---
+import Base from '../../layouts/Base.astro';
+---
+<Base title="Code :: sqzer">
+ <h1>Code</h1>
+ <p>Nothing here yet.</p>
+</Base>
diff --git a/src/pages/index.astro b/src/pages/index.astro
@@ -0,0 +1,9 @@
+---
+import Base from '../layouts/Base.astro';
+import { getEntry, render } from 'astro:content';
+const entry = await getEntry('about', 'about');
+const { Content } = await render(entry!);
+---
+<Base title="sqzer" description="Notes on systems, networks, and the things around them.">
+ <article><Content /></article>
+</Base>
diff --git a/src/pages/papers/index.astro b/src/pages/papers/index.astro
@@ -0,0 +1,7 @@
+---
+import Base from '../../layouts/Base.astro';
+---
+<Base title="Papers :: sqzer">
+ <h1>Papers</h1>
+ <p>Nothing here yet.</p>
+</Base>
diff --git a/src/pages/talk/index.astro b/src/pages/talk/index.astro
@@ -0,0 +1,7 @@
+---
+import Base from '../../layouts/Base.astro';
+---
+<Base title="Talk :: sqzer">
+ <h1>Talk</h1>
+ <p>Nothing here yet.</p>
+</Base>
diff --git a/src/pages/writing/[year]/[slug].astro b/src/pages/writing/[year]/[slug].astro
@@ -0,0 +1,24 @@
+---
+import Base from '../../../layouts/Base.astro';
+import { getCollection, render } from 'astro:content';
+import type { GetStaticPaths } from 'astro';
+
+export const getStaticPaths: GetStaticPaths = async () => {
+ const posts = await getCollection('writing');
+ return posts
+ .filter((p) => !p.data.draft)
+ // ⚠️ params 는 **디코딩된 원문**이어야 한다. 한글 슬러그가 여기에 그대로 들어간다.
+ .map((p) => ({ params: { year: p.data.date.slice(0, 4), slug: p.id }, props: { post: p } }));
+};
+
+const { post } = Astro.props;
+const { Content } = await render(post);
+---
+<Base title={`${post.data.title} :: sqzer`} description={post.data.deck}>
+ <article>
+ <h1>{post.data.title}</h1>
+ {post.data.deck && <p>{post.data.deck}</p>}
+ <time datetime={post.data.date}>{post.data.date}</time>
+ <Content />
+ </article>
+</Base>
diff --git a/src/pages/writing/index.astro b/src/pages/writing/index.astro
@@ -0,0 +1,27 @@
+---
+import Base from '../../layouts/Base.astro';
+import { getCollection } from 'astro:content';
+import { byHugoOrder, postUrl } from '../../lib/order';
+
+const posts = (await getCollection('writing'))
+ .filter((p) => !p.data.draft)
+ .sort(byHugoOrder);
+
+// 연도로 묶는다 — URL 축과 일치시킨다.
+const years = [...new Set(posts.map((p) => p.data.date.slice(0, 4)))].sort().reverse();
+---
+<Base title="Writing :: sqzer">
+ <h1>Writing</h1>
+ {years.map((y) => (
+ <section>
+ <h2 id={y}>{y}</h2>
+ <ul>
+ {posts.filter((p) => p.data.date.slice(0, 4) === y).map((p) => (
+ <li>
+ <a href={postUrl(p)}>{p.data.title}</a>{p.data.deck ? <>: {p.data.deck}</> : null}
+ </li>
+ ))}
+ </ul>
+ </section>
+ ))}
+</Base>
|