사이트가 죽었을 때 내 자금은 어디에 있는가
2026년 10월 23일 호스트가 22분 38초 동안 전원이 꺼졌습니다. 잃어버린 주문은 없습니다. 입금 주소가 저희가 아니라 유동성 벤더의 것이기 때문입니다. 그 503 페이지가 실제로 무엇을 반환하는지, 오리진이 사라진 동안 주문 페이지가 무엇을 보여줄 수 있는지도 실측했습니다.
2026년 10월 23일 veilswap.io를 돌리는 호스트가 전원이 꺼졌습니다. 커널 로그는 08:48:05 UTC에서 끊기고 다음 부팅의 첫 기록은 09:10:43 — 그 사이 22분 38초 동안 오리진 서버는 존재하지 않았습니다. 그 구간에서 얻은 두 가지 사실이 어떤 안심 문구보다 값집니다. 첫째, 08:40~09:30 UTC에 생성된 주문이 0건이라 송금 도중에 죽은 사람이 없었습니다. 둘째, 데이터베이스는 이전의 모든 행을 그대로 안고 돌아왔습니다. 현재 147건, 만료 119, 결제 완료 21, 실패 5, 환불 2입니다. 장애는 캐시에 숨겨지지 않았습니다. 사용자가 실제로 묻는 것은 더 작고 어려운 질문입니다 — 페이지가 열리지 않을 때 내 자금은 어디에 있고, 이 주문을 아직 확인할 수 있는가. 이 글은 둘 다 실측으로 답합니다. (모네로 확인 대기)
그 점검 페이지의 정체. 이름이 veilswap-maintenance인 Cloudflare Worker이며 오리진을 전혀 건드리지 않습니다. fetch 핸들러에 경로 분기가 없어 페이지 요청, API 요청, POST 모두 같은 2,393바이트 HTML을 받습니다. 2026년 11월 4일 10:04 UTC 실측: HTTP 503, content-type text/html, cache-control no-store, must-revalidate, retry-after 300, 그리고 표식 헤더 x-veilswap-maint: 1. 다섯 번의 샘플에서 TTFB는 48~125ms였습니다. 응답이 엣지에서 생성되어 엣지를 떠나지 않기 때문입니다. Accept-Language를 zh-CN으로 보내면 같은 페이지가 2,396바이트, lang은 zh-CN이 됩니다. POST도 503입니다 — 메서드도 보지 않습니다.
10초에 나타나고 10초에 사라진다. 스위치는 Worker가 아니라 라우트입니다. veilswap.io/__maint-probe/*를 스크립트에 묶으면 10초 안에 적용되었고(t+5초 404, t+10초 503), 삭제도 같았습니다(t+5초 503, t+10초 404). 중요한 점은 이 순환의 절반만 자동이라는 것입니다. 감시 스크립트가 5분마다 돌면서 호스트 내부에서 오리진을 확인하고(3001 포트는 방화벽에서 DROP되어 외부 curl은 항상 000), 앱이 200을 주면 점검 라우트를 지웁니다. 추가는 수동입니다. 이것이 솔직히 인정하는 공백이며, 그 정직한 503 페이지가 반드시 뜬다고 말할 수 없는 이유입니다. 아무도 라우트를 걸지 않으면 방문자는 Cloudflare의 522를 보게 되고, 522는 아무것도 설명하지 않습니다. (모네로 확인 대기)
점검 모드에서 주문 페이지는 스켈레톤뿐이다. /order/<id>는 32,795바이트 껍데기이고 상태는 HTML에 없으며 브라우저가 /api/order/<id>에서 가져옵니다. 점검 라우트가 걸리면 그 API 경로도 같은 503 HTML을 받습니다 — 추측이 아니라 실측입니다. Worker에 /api 분기가 없기 때문입니다. 클라이언트 로직은 컴포넌트에 적혀 있습니다: 응답이 ok가 아니면 즉시 반환하고 5초마다 폴링을 계속하며, 주문 객체는 여전히 null이라 화면은 회색 자리표시자에 머뭅니다. 오류 문구도, 라벨도, 설명도 없습니다. 그 본문을 JSON 파서에 넣으면 먼저 예외가 납니다. 의도된 거래입니다. 중단 중 쓰기를 막아야 임시 환경이 실제 주문을 만들지 않습니다. 대가는 분명합니다 — 오리진이 죽은 동안에는 조회할 것이 정말로 없습니다. 오프라인 조회는 데이터베이스 사본이 다른 곳에 있어야 하고, 저희에게는 없습니다.
코인은 그 기계 위에 있었던 적이 없습니다. VeilSwap은 비수탁형이며 코드는 그 말을 문자 그대로 구현합니다. 주문의 입금 주소는 견적을 낸 유동성 벤더가 반환한 payinAddress입니다. 저희는 그 키를 보관하지 않습니다. 그래서 오리진이 죽어도 입금 주소는 계속 작동합니다 — 벤더 자체 인프라가 지켜보고 있고, 중단 중 보낸 입금은 저희가 아니라 벤더가 입금 처리합니다. 저희가 가진 것은 한 행의 레코드입니다: 주문 ID, 벤더 주문 ID, 금액, 주소, 상태. 그 행이 나중에 다시 페이지가 되는 씨앗입니다. 동시에 유일하게 정말 나쁜 경우도 설명합니다 — 주문 ID를 잃는 것입니다. 그것이 없으면 행을 찾기 어렵고, 흐름 전체에서 교환에 영향이 없다고 말하는 유일한 화면이 404 폴백입니다.
세 가지 장애, 세 가지 증상. 오리진 중단: 엣지에서 503, 모든 경로, 48~125ms, 점검 문구. 오리진은 살아 있고 벤더 API가 죽음: /api/order가 200을 반환하며 DB의 마지막 상태와 pollError 필드를 담고, 주문 페이지에는 상태 동기화가 지연된다는 호박색 배너가 뜹니다. 폴링은 계속되고 종료 상태를 지어내지 않습니다. 당신의 회선 문제: 아무것도 해석되지 않으며, 이 호스트에서 잰 1.11초 견적 시간은 기준이 되지 못합니다. 구분이 중요한 이유는 조언이 정반대이기 때문입니다. 첫째는 기다리며 링크를 보관. 둘째는 재전송 금지 — 입금이 이미 벤더에 계상되었을 수 있습니다. 셋째는 경로를 바꾸는 것이지 주문을 바꾸는 것이 아닙니다.
상태는 어떻게 스스로 돌아오는가. cron이 5분마다 vs_sweep.sh를 실행하고(로그에 ok:true와 각 카운터 기록), 종료 상태가 아닌 모든 주문을 생성 시 저장한 벤더 주문 ID로 다시 폴링합니다. 로컬 시계는 생성 후 30분입니다. 벤더가 아직 입금 대기라고 하면 그 행은 local_timeout 표시와 함께 만료가 됩니다 — 오늘 147건 중 87건이 이 표시를 갖고 있습니다. 두 번째 패스는 6시간을 소급해, 로컬에서 만료됐지만 나중에 벤더가 입금을 확인한 주문을 결제 완료로 되돌립니다. 벤더가 전혀 찾지 못하는 주문은 24시간 후 강제 만료됩니다. 솔직한 단서: 이 6시간 복구 경로에서 기록된 실제 사례는 현재 0건입니다. 실적이 아니라 메커니즘으로 보세요. (모네로 확인 대기)
해시는 당신이 보고 있는 동안에만 존재한다. 완료된 주문이 반환하는 것은 상태, 벤더 상태, 금액, 주소, validUntil이며 트랜잭션 해시는 없습니다. 2026년 11월 4일 결제 완료 주문에서 실측한 그대로이고, 데이터베이스 스키마에도 해시 컬럼이 없습니다. 따라서 주문이 종료 상태가 되면 해시는 저희 쪽에서 사라집니다. 남는 것은 벤더 기록과 당신의 지갑뿐입니다. 여기에는 숨기지 않고 짚어야 할 작은 모순이 있습니다. 점검 페이지는 복구 후 주문 링크가 같은 상태와 트랜잭션 해시를 보여 준다고 약속합니다. 상태는 보여 줍니다. 해시는 보여 주지 않습니다. 송금하는 순간 지갑에서, 또는 확인 중일 때 주문 페이지에서 txid를 적어 두세요. (모네로 확인 대기)
장애 당일에 할 여섯 가지. 첫째, 주문 링크를 보관하세요. 유일한 열쇠입니다. 둘째, 지갑을 닫기 전에 본인 지갑에서 입금 txid를 적어 두세요. 셋째, 같은 결제로 두 번째 주문을 만들지 마세요 — 원래 주소는 여전히 작동합니다. 넷째, 화면이 회색이라고 재전송하지 말고 먼저 체인을 확인하세요(BTC, TRC20, ZEC, LTC는 공개 탐색기가 있습니다. XMR은 없으므로 모네로는 벤더 주소의 움직임이 유일한 외부 확인 수단입니다). 다섯째, 사이트가 돌아오면 링크를 10분 간격으로 두 번 여세요 — 움직이는 상태가 살아 있는 상태입니다. 여섯째, 계속 이상하면 주문 링크와 txid를 붙여 [email protected]로 보내세요. 그 메일함은 다른 인프라에서 돌아 오리진 장애의 영향을 받지 않습니다. (Zcash vs 모네로 2026 비교)
저희가 약속하지 않는 것. 그 정직한 503 페이지가 항상 뜬다고 약속하지 않습니다. 라우트를 거는 일이 아직 수동이기 때문입니다. 오프라인 주문 조회도 약속하지 않습니다. 데이터베이스는 오리진 위의 단일 사본이고 매일 04:00 UTC 백업은 복구용이지 조회용이 아닙니다. 열어 둔 페이지가 재부팅을 견딘다고도 약속하지 않습니다. 폴링 대상이 방금 죽은 그 오리진이기 때문입니다. 성립하는 것은 더 좁고 검증 가능합니다. 입금 주소는 벤더의 것, 주문 행은 WAL이 켜진 SQLite에 살아남고, 폴링은 벤더 ID로 재개되며, 완료된 교환은 체인 위의 사실이라 저희도 벤더도 고칠 수 없습니다. 페이지와 체인이 어긋나면 체인이 맞습니다.