# v2 git bundle
4acc65c6d4f84d0c5c2faad94fdd7deaec5ac542 refs/heads/bundle
4acc65c6d4f84d0c5c2faad94fdd7deaec5ac542 HEAD

PACK      ¤œxóþtree b1c7ed692ab2271e3f30a430260c0dacb79a9888
parent 3c6e210360ac381d850852d8e658d4e027363878
author Zach Kelling <z@zeekay.io> 1778045579 -0700
committer Zach Kelling <z@zeekay.io> 1778045579 -0700

ci(docker): amd64-only â€” multi-arch QEMU stalls on heavy SPA + Go
ÜP^’xýþtree 5fb3ec9b9f4ec2dcf69999cd72a5a5b490e7e538
parent 494e3fe64555b1487ba2c8547140abc3d94e6954
author Zach Kelling <z@zeekay.io> 1778043245 -0700
committer Zach Kelling <z@zeekay.io> 1778043245 -0700

ci(docker): go build -mod=mod populates go.sum on the fly
 4NÆ™x	öþtree 7b59cbbaa57ea015fe68d85dac4d25345445750b
parent d41699bb3361f78f0a9ee3ade771360809d5ce9e
author Zach Kelling <z@zeekay.io> 1778043048 -0700
committer Zach Kelling <z@zeekay.io> 1778043048 -0700

ci: drop ci.yml â€” Docker workflow handles cgo build via alpine
YR�“xüþtree a2df3925c59af70f8680d33b1bc8888e18b55537
parent d17d22721eeed9cceaa3265313eb8b54dbbdd046
author Zach Kelling <z@zeekay.io> 1778042662 -0700
committer Zach Kelling <z@zeekay.io> 1778042662 -0700

ci: ARC has no_new_privs (no sudo); GOSUMDB=off everywhere
*œNÆ�xð ÿtree bad4bf082412b6cb2f5223dd0be204330b9850ee
parent 7f0765d1127a67fb76e6922a8c672fcf75cb4087
author Zach Kelling <z@zeekay.io> 1778042082 -0700
committer Zach Kelling <z@zeekay.io> 1778042082 -0700

chore: gitignore .claude/ runtime state
OHž˜x÷þtree 860487c5a72962cf6ab735df02dc6c8e3e119326
parent c979f90b2a92166c1875cf80308c9ea7b3c1986c
author Zach Kelling <z@zeekay.io> 1778042065 -0700
committer Zach Kelling <z@zeekay.io> 1778042065 -0700

ci: regenerate go.sum at build time (proxy cache inconsistency)
PÝ—xøþtree abf969e54029a2f0e07558ff0fe2fe26203f078e
parent 33658ed9cfea935b4f1b0921d86f6556ae030f91
author Zach Kelling <z@zeekay.io> 1778041088 -0700
committer Zach Kelling <z@zeekay.io> 1778041088 -0700

config: full chain list â€” all Lux native + subnet + external
`ÜQ ™x	öþtree ab972f511d87cebfd24c8fec81cf93ffc1e27d23
parent 89c9cd2762bb86f349e695ff8291b924aa83a45b
author Zach Kelling <z@zeekay.io> 1778040880 -0700
committer Zach Kelling <z@zeekay.io> 1778040880 -0700

ci: refresh go.sum (proxy hash drift), install gcc on ARC runner
Ê`QÂ“#x3Ìýtree 0fc515d6feb88821662d1b110955301bde2fcc95
parent 9b8044b665e0e12d0ce4808d7b5a3194b62a0939
author Zach Kelling <z@zeekay.io> 1778040555 -0700
committer Zach Kelling <z@zeekay.io> 1778040555 -0700

fix(go.sum): align hanzoai/replicate hash with proxy.golang.org

Local go env had GONOSUMDB=...hanzoai/* which made `go mod tidy` use the
GitHub-direct hash (YxjIynlâ€¦). CI fetches from proxy.golang.org which
returns a different hash (5Rl3â€¦) because the proxy cached a slightly
different zip layout. Replace ours with the same value indexer/graph
already use.
úN»¥”!xëýtree aa632e6aa6b40cbaced617b1d2f1945e97b3087a
parent 9c1c9c7683028ed3cc2f0d7f364be29f98adc080
author Zach Kelling <z@zeekay.io> 1778040340 -0700
committer Zach Kelling <z@zeekay.io> 1778040340 -0700

ci: align with luxfi org's actual ARC pool (lux-build-linux-amd64)

Mirrors the luxfi/indexer change: switch all workflows from the
nonexistent hanzo-* labels to lux-build-linux-amd64, inline the multi-
arch Docker build (arm64 via QEMU), drop the cross-org reusable, drop
the workflow-sanity job that referenced cross-org actions.
±µ±Ÿ\xÏ0útree db2b2f2448cf0d23ad32c220273256d39a0a16d4
parent 77c543529838e6db25f92f47f88291585cb379b8
author Zach Kelling <z@zeekay.io> 1778040232 -0700
committer Zach Kelling <z@zeekay.io> 1778040232 -0700

unified explorer: single Go binary with embedded SPA + per-chain graph

Module renamed to github.com/luxfi/explorer (was -unified). Imports
luxfi/indexer/{daemon,evm,storage,explorer} and luxfi/graph/{engine,
indexer,storage} as libraries; embeds the luxfi/explore SPA via go:embed
all:static.

A new ChainSupervisor wraps the existing ChainRegistry: adding a chain
(via config / mDNS / admin API) spawns a per-chain indexer goroutine plus
one graph engine + indexer per enabled subgraph; removing cancels them
and frees the routes from a sync.Map dispatcher mounted on /v1/indexer/,
/v1/explorer/ (legacy alias), and /v1/graph/.

Config schema is fully per-chain: indexer poll interval, list of graph
subgraphs (each can pick a built-in schema like amm, securities, fheâ€¦),
brand (name/coin/accent + on-disk icon/logo paths), token features, and
top-level networks list. /envs.js exposes the chain list and active
brand to the SPA at runtime, so a deploy can rebrand by remounting
chains.yaml + assets â€” no image rebuild.

Dockerfile clones luxfi/explore + luxfi/indexer + luxfi/graph as
siblings inside the build, populates ./static from the SPA build, and
produces one binary. CI checks out the three repos as siblings so the
local replace in go.mod works without tagged releases.
#¥Ö›xôþtree e9ef4f120b79eaea5dcdbc67fc3b7a64d84a2f64
parent 0c29cc0a65548b9d3700f3d43928b4cff4c6c153
author Zach Kelling <z@zeekay.io> 1778036879 -0700
committer Zach Kelling <z@zeekay.io> 1778036879 -0700

chore(.gitignore): proper Go ignores, drop rebuild trigger comment
¨íSÒ–xf™þtree a1f2c5fd9e7fc38a9e762f7514b9a9a8e6b4a78d
parent 630ffa251a6398d79e96d2b39ec5855cbd3df0d6
parent 755b76ac22240f7aa06d5d0b0b940f3eb1cade0c
author Zach Kelling <z@zeekay.io> 1778036368 -0700
committer GitHub <noreply@github.com> 1778036368 -0700

Merge pull request #2 from luxfi/ci/multi-arch-native

ci: build multi-arch (amd64 + arm64) on native runners4_oØ˜xø ÿtree a1f2c5fd9e7fc38a9e762f7514b9a9a8e6b4a78d
parent dd387f3433d8725948027b5520c0d610b339a48e
author Zach Kelling <z@zeekay.io> 1778036307 -0700
committer Zach Kelling <z@zeekay.io> 1778036307 -0700

ci: register hanzo runner labels for actionlint
Á‰KíŸxï ÿtree 02aed7906e41349de231ca755ff5d96fc6f8daf9
parent 8e4e88c1d09a11263d6b873ae8274b2598cc07b5
author Zach Kelling <z@zeekay.io> 1778035887 -0700
committer Zach Kelling <z@zeekay.io> 1778035887 -0700

ci: use hanzo-* runner labels (sanity)
>jGñœx,Óþtree d5a98873ed8293956340c809fffad804ae7a3d36
parent 0531a94d58c5b3881b12a8ef1bcb9a86a00ff550
parent 630ffa251a6398d79e96d2b39ec5855cbd3df0d6
author Zach Kelling <z@zeekay.io> 1778034163 -0700
committer Zach Kelling <z@zeekay.io> 1778034163 -0700

merge(main): keep multi-arch + semver-only triggers
¨|Z¬�xÀ?þtree d5a98873ed8293956340c809fffad804ae7a3d36
parent b35ef6b747fe94d1dd2b2d9c5ac4bef9f7ec1e92
author Zach Kelling <z@zeekay.io> 1778034119 -0700
committer Zach Kelling <z@zeekay.io> 1778034119 -0700

explorer: remove Liquidity branding cross-contamination

Drop the _lqd._tcp / lqd / liquid daemon entry from the mDNS service
discovery list. The lqd daemon is the Liquidity-side white-label node
binary; the OSS Lux explorer must not advertise it.
"q”Ø–x¦Yþtree 0b695143561ce964ded453a18b176ef8db3bddc2
parent 579d74c09a8664b3e10c1ade5217a0697471e10a
author Zach Kelling <z@zeekay.io> 1778033309 -0700
committer Zach Kelling <z@zeekay.io> 1778033309 -0700

ci: build multi-arch (amd64 + arm64) on native runners

Drop branch triggers, semver-only tags. Pin lux-build-linux-{amd64,arm64}
ARC runners. Eliminates the amd64-only manifest that crashed Apple Silicon
local k3d pulls.
‡™‡˜xøþtree 7fd06b2687f41531672d96953f410837b9ed557f
parent 5e1d5c3f0337d04b4711bc2e5ca5af160f549757
author Zach Kelling <z@zeekay.io> 1776997394 -0700
committer GitHub <noreply@github.com> 1776997394 -0700

ci: migrate to canonical docker-build reusable (#1)

- Add docker.yml using hanzoai/.github/.github/workflows/docker-build.yml@main
- Add workflow-sanity.yml to enforce canonical CI contract
- Remove bespoke docker build steps from existing workflows

Refs: hanzoai/.github canonical Docker CI contract.ÑP¦û“xóþtree 7fd06b2687f41531672d96953f410837b9ed557f
parent 5e1d5c3f0337d04b4711bc2e5ca5af160f549757
author Zach Kelling <z@zeekay.io> 1776997280 -0700
committer Zach Kelling <z@zeekay.io> 1776997280 -0700

ci: migrate to canonical docker-build reusable

- Add docker.yml using hanzoai/.github/.github/workflows/docker-build.yml@main
- Add workflow-sanity.yml to enforce canonical CI contract
- Remove bespoke docker build steps from existing workflows

Refs: hanzoai/.github canonical Docker CI contract.
÷·¥mžxþ ÿtree 05ec30403163c08342bb6aadcf78f72d50d2ce55
parent e8c2d51752df366e487b76a5f33d9b5eb357747c
author Zach Kelling <z@zeekay.io> 1776374133 -0700
committer Zach Kelling <z@zeekay.io> 1776374133 -0700

ci: docker multi-arch build + semver release workflow
\ýM­œxü ÿtree ae8c1cbf01a3c4ef3b64f9442bedf313a5697b83
parent 90248d6c7bb2cae223a57b231d8d9bffb614e78f
author Zach Kelling <z@zeekay.io> 1776077660 -0700
committer Zach Kelling <z@zeekay.io> 1776077660 -0700

fix: use lux-build runners, not generic self-hosted
ÁON<’xíþtree 67410a2f862abb06a0545f03e1f65d5d0e6869e3
parent 63a8610d3ebacf64a2634f38cccdf744d28990a6
author Zach Kelling <z@zeekay.io> 1776073381 -0700
committer Zach Kelling <z@zeekay.io> 1776073381 -0700

feat: use info.getChains for single-call discovery (no per-chain probing)
·›TÂšxåþtree 0ff63ccea78bbb19a3597efae560484f7e17f0da
parent 82926899b7f341b085a4b12cfe0794dfe7a49a6e
author Zach Kelling <z@zeekay.io> 1776071785 -0700
committer Zach Kelling <z@zeekay.io> 1776071785 -0700

fix: probe RPC before registering â€” only index chains this node actually tracks
xƒY¶šx
õþtree f80df6b97507f299c3c70d1c113711d00044b827
parent 672dc81e5757d00125d361f4dd4c58f5dd324fe3
author Zach Kelling <z@zeekay.io> 1776070690 -0700
committer Zach Kelling <z@zeekay.io> 1776070690 -0700

fix: fully dynamic chain discovery â€” zero hardcoded chain lists
£}R~™x	öþtree 5c2106f955decb2bce0e4bed364ab821991d67a8
parent cca50859f0645a03a0885679c96d588eda838b20
author Zach Kelling <z@zeekay.io> 1776069740 -0700
committer Zach Kelling <z@zeekay.io> 1776069740 -0700

fix: per-daemon chain sets â€” liquid has evm/dex/fhe, not C/P/X
½Q*Ÿxÿ  ÿtree 3a06e8dfee3ba02cf9b38c1052b2b0a3bca08186
parent bf584f6b48f9d265640f12d162b47250d18b1699
author Zach Kelling <z@zeekay.io> 1776069577 -0700
committer Zach Kelling <z@zeekay.io> 1776069577 -0700

feat: discover luxd, zood, lqd, hanzod, parsd via mDNS
ÓZMŸ'x€ýtree 453428f8c721029ab777cb81e7d366459207548d
parent ed8fb4b646ba370fab3b53583760b376f53d15b1
author Zach Kelling <z@zeekay.io> 1776069391 -0700
committer Zach Kelling <z@zeekay.io> 1776069391 -0700

feat: runtime chain registry, mDNS discovery, /v1/explorer/realtime

Runtime-mutable chain registry with admin CRUD API, mDNS auto-discovery
of local luxd nodes, and multi-chain WebSocket realtime subscriptions.

Config sources (priority order):
  1. Admin API (POST/PUT/DELETE /v1/explorer/admin/chains)
  2. --config chains.yaml
  3. CHAINS env var
  4. mDNS _luxd._tcp discovery (lowest, never overrides manual)

21 tests, all passing.
2Î˜›xë ÿtree 200ce12a53e92c05fa83f229e3fa078c9b474bce
parent 1a6d559ccfc01cdd2521511839f04334772dc735
author Zach Kelling <z@zeekay.io> 1776010884 -0700
committer Zach Kelling <z@zeekay.io> 1776010884 -0700

ci: trigger combined image rebuild
ÆUFŽ�xòþtree 80e2a7ada6b68d378a94574f2119a05b13b746d3
parent 7ee5150d270ca574c8e8f555a7048da9564df2bf
author Zach Kelling <z@zeekay.io> 1775943083 -0700
committer Zach Kelling <z@zeekay.io> 1775943083 -0700

docs: full README, compose.yml, chains.example.yaml, healthcheck, CI
ÑâQ¬“$xC¼ýtree 7dff837e60515e0be8a2360b832f716818c1a67a
parent cd4378e3f92324cd031ab01fcd584be7242e23d2
author Zach Kelling <z@zeekay.io> 1775940607 -0700
committer Zach Kelling <z@zeekay.io> 1775940607 -0700

feat: include graph engine (subgraph replacement) in combined image

4 repos, 4 images:
  indexer  â†’ chain data (blocks, txs, tokens, contracts)
  graph    â†’ subgraph events (swaps, pools, DeFi, GraphQL)
  explore  â†’ Next.js frontend
  explorer â†’ all three combined (this)

Single image contains 3 binaries: indexer, graph, explorer.
Each can run standalone or together.
†ß¿_˜x÷þtree 30ec1903c50c309e778ff050ac500e7a227274c6
parent a2b779c20cbdce6ac8edd2ebf2a5e89e307f588f
author Zach Kelling <z@zeekay.io> 1775940007 -0700
committer Zach Kelling <z@zeekay.io> 1775940007 -0700

fix: use pre-built indexer image as base, layer frontend on top
øõQÀ”%xT«ýtree 65df319ce49c0c030046fb802b76864890d0b6bb
author Zach Kelling <z@zeekay.io> 1775939328 -0700
committer Zach Kelling <z@zeekay.io> 1775939328 -0700

feat: combined explorer image (indexer backend + explore frontend)

Three repos, three images:
  luxfi/indexer  â†’ ghcr.io/luxfi/indexer  (Go backend, API + indexing)
  luxfi/explore  â†’ ghcr.io/luxfi/explore  (Next.js frontend)
  luxfi/explorer â†’ ghcr.io/luxfi/explorer (this: combined single image)

Dockerfile pulls both repos at build time, embeds the Next.js static
export into the Go binary via go:embed, produces a single container.
uùËV¥&x31 ½ôÌ’ŒÒ$Õc5ßc={’².[®9a%ñÀêµ¡��™‰	XIfz^~Q*ÃÄ·QÝÉaŸŸÝÍŒ2?È¼QøªÑ®PU.ùÉÙ©Ei™9©?âÔÂÖê~ÛßÊ|^8yÃóï÷ ª‚\]|]õrS–ë›œl/hièSøuÁ_™(ÑPEÉ‰™yÅz©‰¹9©z•‰¹9ámœýÍn[é¯�j|áYnËŒ¿[aÊósò‹�ê€ÊúÊ
þ�[8ÿùt·y³¥ŽgÏY_YÌU–ž¯—›ŸÂ lð�aëKîGÒ¦Ÿ×ÕÜç¶v:ÄƒPQ\šËÐtðrfóæ«>9wvŸ6µOÓˆ;ÍU‘t˜^z>Ã;¥è¿wUßrðìIórýÜ«úaJR€N*aðãªd“2ØTâ´”cMÚáÕßì�JÌMM
K’3tSs“RSôŠ3ÌÔÎ®–ÖˆlŠg/µ/÷ú£µþkØ¨aE©‰9%™¹© .¬ù³°Cni‹¶ª¿fÁ¯/«þ )‹/I-.©íxüÐ¨y‡îâÕ—>8×÷¼qì{Z	®6=³¸¤¨¤,+ò×�ÇjïZâxéeUdËÊ`4ep#Ÿ8¸ø“öªÚ¨´½,™û=›%Kfê›€“PqrQfAI1Ã='«%_þíò«³-Wé·méˆÓvÝ ,Ýô‡ëƒx+ Ôÿåå"40000 .github jÊÍe}™Z²‘Lööuú°£‘Ö±"CdÈaë:x+ Ôÿåå"40000 .github ™ù“Ì#+«{"È»9Š<;'Ý"±"CJS`ë9x+ Ôÿåå"40000 .github ^œù$Ë':r»y.ª™¯bI‚‘±"CM¢Çà9x  ßÿåå°__Öˆm2mÀWw1<&vÜ¢XN°“sòÙHIë.x+ Ôÿåå"40000 .github *ßŽ%w@KAë‰­¬<‚%êf±"C9Ìë9x+ Ôÿåå"40000 .github m¦ÏÊÖÁë­“P¨Ï�‰¤½ü±"CnF¹ï9x àÿåå�4‡ý‹õ(EßÝ2Õ£þ_”uè)°±Hðëì#-x<ÃýåÑ40000 .github ¾�òs¬åóÎ£jQ’Äö�ÊJhÝ100644 .gitignore EyTHò)Ïþ<WÍ®\wÝè100644 Dockerfile -YË!Afip„·H•Þt100644 README.md oƒ&;bk…èÉÜ›ƒÛV›vÿ4100644 chains.example.yaml Ñdeû.¤"?Ø�JÀâö100644 compose.yml àÓõ>”)S¢7>ðr¹Âš çÑ100644 config.go Ø½#N!,¾y«¾˜º¢¡ªä^Ÿ‘R100644 frontend.go ˆ3‰d�=­‹�<@>°5ßc&100644 go.mod m(Å›¯S¹žÂ^nãb‚àâ100644 go.sum 7uJ	û«œ‘âž!*‚Ž§gr100644 main.go BÁoX8Vøïâ0þ_9BõL““P&“ c@÷o:Åº±Z©jø¬šß+F)p�L100644 registry_test.go úäYs²¸5ÀZ ö½C±“C"J40000 static 0ûÄûƒÁ�1~ótHŠãT×4 ©100644 supervisor.go b|3ŒyaGŽ-,ûJÇ9'P5¢Ü£ëƒJx+ ÔÿÑÑ"40000 .github MxC{Vä¯±Q±cƒƒÊkhé§±"¯K`×á:x! ÞÿÑÑ°eÓƒÝš»wÝ¬dZ+Þq9ÏñÈ>³yXˆEå/xE ºÿÑÑ"40000 .github &Ò›_#ò{(ãaê*ðêÐºŠƒ±"C7uJ	û«œ‘âž!*‚Ž§gr³yXœ!7ïSx àÿÑÑ�®Ò[$rQ”³j¦Ù“ÄU!ÆÐ±ÂÖô[å-xe šÿÑóD40000 .claude äó,õ9ã‰ÿ…ž;·- åu40000 .github .õB��Þø·ˆô41œƒùd“*‘"8á_E§�Àf»Âs¦e`Rô¶óø²±ncüõ+Ëàsx  ßÿóÑ‘"4¬ž'ê	‰×Ã€Ê!6ÐÂœã�¶>±j‰óZGã.xC ¼ÿÑÑ"40000 .github ”Ÿ~?×¦~¼®ºSñ<�¼µë›Q‘"8lŽ¼àÅÉêp¹æSÑ@jw±nc¢>ëQx+ ÔÿÑÑ"40000 .github ÓÇ,žd/`ìjÒ*^ÇëøNm±"¯K¨ï9x àÿÑÑ�ZL`>$‘T,]$ˆéÆ.�YøIuq±ncÁ¨bë-x+ ÔÿÑÑ"40000 .github «!r<M…S)0Úã=�Š—ÐÙÍ±"¯=v]¯xO °ÿ100644 actionlint.yaml !U¡išQà›â‡Fµ°˜!>F40000 workflows 4:õÌ9ž?H‹,Qs�x÷áßÆ\yŽé\x æÿOO�;®…–“M9æ¯ßg|çR%¬“æŽâé'x æÿOO�;uÛzµ]8œÇÖ4m“©0WÊ­‚”
ëé'x æÿOO�;©’ó'ôMaÂ:Cu:íëjh|ã
Né'x æÿOO�;ùHzYÔÿÌ¥?•øü„{Um·šoFé'x æÿOO�;ÕY ƒ\`¶0èÕè®†�î›òÍ† šºx: Åÿself-hosted-runner:
  labels:
    - lux-build-linux-amd64
D8ô¦x& Ùÿ100644 docker.yml £³×—87ä:ße³ >£o²C,¡³Xxœ�TMoÛ0½ûWØ€CìÖõ` @÷QlÃÐÛÚÓ0ŠMÛjdÑ“h'A–ÿ>ÊN[gMŠæš|O"IYUc
([ ‹È¦À’Ü¢0´¼ÉµogUp6­ïÿX•>…_'Ý«“ßQÔ «µ÷š¬áŒ,£e8Tyà©l¡JÇÒiFñè<fZ ½óD/à²5¬cå²
È‚iW…rå‰«Xwo¼×Z‹"“€rõÙ)tZÁ÷‹Ëkh-kJŽÚ†øŽrKÚzà
wäŸXwâ…È}f�D·4ïÓÏ{†:…ìcQ$äÏ[mòØh+¶ªó³Óâ? bh}¨SeÔ˜ffjù¼;ý1Ü2õÈmÿÁº�Îy÷z‡”&h®Òû/ÑÑ(.ÈÕBï³˜ö>upŸòêGçNswì8´PiÑí$¦&tÖ"‡™Uäùp†JmŸq³ÃR{vëÊ*s‰¦QLt¶Î—›”Âlç‰Ã†¼frëZ†Ön·c�”÷’Y>P<fÙ'?_}º~wsõíËÅ×|,s˜B�¬¢Ñ�%„H®XÝWñæhº|WÃ´Ÿß)®CnOÅÂ¨ŽDhi%zž©–Ç{õwäßºÁ™ïÇu"«È"Él³‘¯°p{µÃÖê–Üv›ˆ¥m°p*5izÌøpK‡¯À½"gGé_�§�<9Áa�&�§9üŒš£ñ»F†-KBCY§¦•žáýîú!ô»¾Ï”,h\8ªÓA†Rdð‰u ušé(vRSŽ³Z­Æ;êÐ*›ÉÊxÇÂ{
ìZŒþ(¸°ˆêƒrxz …ÿƒÁ
�…
amd64-only‘�.Ywill be added back
# once a native arm64 scale-set is provisioned (CLAUDE.md: no QEMU).
#‘óŽ³Ú¦“Œ÷"ò*Ê¸x8Çþ# Binaries
bin/
explorer
explorer-unified
*.exe
*.test

# Coverage
coverage.out
coverage.html

# Go workspace
go.work
go.work.sum

# Editor
.idea/
.vscode/
*.swp
*.swo

# OS
.DS_Store

# Local
.env
.env.local
*.local

# AI assistant context (not committed)
AGENTS.md
CLAUDE.md
GEMINI.md
QWEN.md

# Runtime
logs/
8Tcì�Ex, Óÿ¸±# Binaries
bin/
/explorer±)	.claude/
=Û´ÀxÅVQsÚ8~÷¯Ø£�Ò»«ì@ÚNË„Ì0	SŠ9Czé´FØëb,×–®Mfîéžïá~aÉ­d;i2¹·ò h%}’v¿owA˜mWÜbÛ8	KàÛ_ÿB_l–<b~xä³-Z…uBã ‹…°JD$Yä�ŒG °¼ÄäÂÊÑÊm ßþþ¼€ò|*)<]†Â;OŸ�Üª/qÎ"üõ*¡žL¾+?�ÐXi¶Ìì‚E2…§é%�""ÄŸògp¬îúÛø.´òú9Ú„m¥ùGZ{ÌwOÙ9F?xÚ† ƒ„1¼yî)í›z�²Ä"Â»�G#\™
H²RI#Ÿ†8+‘h¿Æè=µ.õhÈ£µi=÷Ž�Å;Û��œI·e¶_jÛÄØ;k»­m£ÉÀ>³Ý…kO�n eœv,kÍe�-M¼ÃÍ@˜8qk×°»Á¨äGº½éÉ@Úé;˜ãbGÄ>›Ž×~ ¦ðæ¨ÚU@!~`&éšA«Gý]€ô¤1t�·	Ÿu­ûæšÐ0FâBo¶Û¡_X3un††{:Ÿõ}„�ñ0Ž”Ë~wÜ7ƒ‘�c½�àé(â³XÝîY&4ò‚îã¯Õ9Ãk¨§Î5˜ÁC•ÅÔ;ÑeÈàÉˆ£xƒŒ@Š ¯Ae}aQBYñ�öäLì³ùbzz4õ½éhqÔ›Ù‹io~Òµ.Z;ñæKYœé4ë6ÙÐ-¡OR<•‘”aÝW­×í†¾MÊðÍ©%2g²ÍŠ©ÍÊˆq‰lZˆ”dP°+¾VÊ¹º™dLƒéw,u¼êæÍ¹Ï ±ö¢‚Toþ dµ3X:_˜�Ü„ðIA~4”Þ™hü4púó÷SÔüáAñÍ¨x ¹Ù¡]ä®+X�ž]
ÿÏÃ«øÑûšpx×©ÆMÚµKÚÀDHÎÒóÖ"¤Ñ¹WiµÎ<í–ÜÒ[A¼J¶Ã[Ú»1¼Ÿ•ž›,‘}�~¹dúo)ð2wà´zT®ž"Wt.ŠúPºvaEéjXQz-`#|ðÅe
ê}gú>çñ¦[–‰�×!6K,oãï˜k!ë$÷êœl¦W˜ÐD‡m–HÒ|ÞúøKÝˆzéäWÍ{pËóM¼dûÐB·XQ†2«´�q´'½£±=@9«Q8î+õcLÊöp4¶_>_ÌœS·o7 ±ršç<%¡¿
é:ÅõH“K g r«yÁ’”‹óB•ëRªt˜ÚE<,ýþq�]•ËïS­ŠCº TÊ‡ÿƒ?»R¡ØSîçŽBüÐï)¢¡ÿë§×JÑ~Ü,’|Ãj™ /7�}³ÝÒ¡»£Œx”x,‘|Å=*1�šù2½!”"kÜd‡•¥‰……€†uÒÜ¿¯ŒÜÚXÚïß™¿ÿÖ¶<ÈÈ9M.¬ReFîßÏRlÉ H/_¼Øo—mcbœÎl·½sÆ§om°Ts¨«× 7ï-°ävµ	Næóé¢7¸ÝÎ«½×{XgfƒúÏ÷ööãÄî�ç'ý»ÿÃ±'M.hØÝßS[…N÷…`¦H$‰YÂ…ßmá¼Roÿí .×Lùì¬02Æ6J{<©Ô‡ªjÊ@¥¶ÅúßR¢°ÍB¬—ª¹“Ø.lpÄIÞê–î†§…°kýsé*Õœ:˜!î3¸Ä¾­ƒ	Ô;G·)DÓ4w½7)P½œ�$öÔ¨}Dò@Ój½=™»ï§Îh2‡�òŽ�OÆ~DØùà=‰CxœUR]kA¥MCíª(
Ò*È¥•ÐÚîn«R¤6Ò´‰±´vCB•B¡Lvg“±»3ËÌl>*’Ÿ|æÅw|ôÉ7ÿ€ÿÁ ý‚7µva—™»gÎ=÷œy7ýùFoö9`7í í$‘�T‹Iƒ:Pc¼Q(¨3Ndw	h\§A€ðZ¥°Œ´ƒðEk’$M 
"V—D2ª(RäëB›iü~“0®œ.‰#˜(oB‹Hµ „�P©˜ÒHåD“'€4XVm’@½’Æ"å] ZÄ–Šjµ\ ¦ž²( N)j4ïÇ\³=^6_Ç>ÜEZ¨iœVÖ`ˆÔMÚÅ|ÿn>eÖÍ¯ÌŒ‰2«ÈåÌÎÄÁ•@øº›Phê82ß&fn¬,ÈÏJ!ôìÓu÷ætâþc?œž2²C±z„ŠQÝy»tdøÐR³�M®ªexYªÖ¶½½|@[æcöÑUë•WÝ)nWÁUÒ7²…ëgä¹Z `
—{ ÷Þ”«…Êó£j©â½Åò ³uá¸{¬µåUÀg¸°íPŠ8�®)zí’$qEªÁq•&šùÚ–HÑé:Þp„Dû‰ß¤
gõïõ#£\G]1Ò&á'‚0WbðÌ'šn´–�Ugiæ†T"*^E�¢²Eá¼xÂ<®�yÁ;A_HD’FÈ£ !•ÆHÔ—í&[Tö–$}f6‹éàf•½Úþ‹âf^„!¨c–(dA®3I¡”�sùÇÊ±ªû{ c°ÃQ×¾å[eï¨´WØÜ-ó+ƒÝÖ³Ý‚¹=yçÁÿüƒdðØHƒ­%‹‚÷ÿßH}„Ù˜\¶‡P [@ßô!a:oëÃ5�Æžò²ÐnPmN§neo~NÍšŠõÚü°Róûòô_`AåµÖxešê# Explorer

Single-binary Lux explorer: chain indexer, GraphQL engine, and SPA frontend
in one Go process. Imports [`luxfi/indexer`](https://github.com/luxfi/indexer)
and [`luxfi/graph`](https://github.com/luxfi/graph) as libraries; embeds the
[`luxfi/explore`](https://github.com/luxfi/explore) Next.js build via
`go:embed`.

```
ghcr.io/luxfi/explorer
  -> indexes one or more EVM chains  (luxfi/indexer)
  -> exposes per-chain GraphQL       (luxfi/graph)
  -> serves embedded SPA + REST API  (luxfi/explore)
```

## Routes

| Path                                              | Purpose                          |
|---------------------------------------------------|----------------------------------|
| `/`                                               | SPA (embedded; SPA-routing fallback) |
| `/envs.js`                                        | Runtime config `window.ENV = {...}` |
| `/icon.svg`, `/logo.svg`                          | Per-host brand assets (disk override) |
| `/health`                                         | Service health                   |
| `/v1/indexer/*`                                   | Default chain explorer API       |
| `/v1/indexer/{slug}/*`                            | Per-chain explorer API           |
| `/v1/explorer/{slug}/*`                           | Legacy alias for `/v1/indexer/{slug}` |
| `/v1/graph/{slug}/{subgraph}/graphql`             | Per-chain, per-subgraph GraphQL  |
| `/v1/explorer/admin/chains[/{slug}]`              | Runtime registry CRUD            |
| `/v1/explorer/realtime`                           | WebSocket realtime hub           |
| `/v1/explorer/realtime/stats`                     | Realtime stats                   |

## Quick Start

```bash
docker run -p 8090:8090 \
  -v $(pwd)/chains.example.yaml:/etc/explorer/chains.yaml:ro \
  -v explorer-data:/data \
  ghcr.io/luxfi/explorer:latest \
  --config /etc/explorer/chains.yaml
```

Or with compose:

```bash
docker compose up
```

## Configuration

`chains.yaml` is the single source of truth. Every aspect of the deploy --
chains, branding, GraphQL subgraphs, network switcher links -- is per-chain
customizable. See [`chains.example.yaml`](chains.example.yaml) for the full
schema.

```yaml
data_dir: /data
http_addr: :8090

brand_default:
  name: Lux Explorer
  coin: LUX
  accent_color: "#5cf"

networks:
  - { label: Mainnet, domain: explorer.lux.network, chain_id: 96369 }

chains:
  - slug: cchain
    name: Lux C-Chain
    chain_id: 96369
    type: evm
    rpc: https://api.lux.network/mainnet/ext/bc/C/rpc
    coin: LUX
    enabled: true
    default: true

    indexer:
      poll_interval: 30s

    graph:
      enabled: true
      subgraphs:
        - { name: amm, schema: amm, enabled: true }

    brand:
      name: Lux C-Chain
      accent_color: "#5cf"
```

`$ENV_VAR` substitution in `rpc:` / `ws:` lets one file template every
network -- mount it read-only and inject secrets via env vars.

## Per-Network Customization

The same image powers every deploy. To rebrand for a different network:

1. Mount a different `chains.yaml`.
2. Optionally mount `icon.svg` / `logo.svg` at the path referenced by
   `chains.yaml` -- they are read from disk per-request, not embedded.
3. Set environment variables to fill the `${VAR}` placeholders.

No image rebuild needed. The SPA reads chain list and brand at runtime from
`/envs.js`.

## Runtime Registry

Chains can be added, updated, and removed at runtime without restart:

```bash
# Add
curl -X POST http://localhost:8090/v1/explorer/admin/chains \
  -H 'Content-Type: application/json' \
  -d '{"slug":"new","name":"New","chain_id":123,"rpc":"http://node:8545","type":"evm"}'

# List
curl http://localhost:8090/v1/explorer/admin/chains

# Remove
curl -X DELETE http://localhost:8090/v1/explorer/admin/chains/new
```

Adding a chain spawns its indexer + (optional) graph workers; removing
cancels them and frees the routes.

## mDNS Discovery

Set `EXPLORER_MDNS=true` to auto-register chains advertised by local Lux
nodes (`_luxd._tcp`, `_zood._tcp`, `_hanzod._tcp`, `_parsd._tcp`). The
explorer queries `info.getChains` on the discovered node and registers each
tracked chain. mDNS-sourced chains never override config-sourced entries.

## Storage Layout

```
/data/
  cchain/
    query/indexer.db         (SQLite WAL, indexer)
    {zapdb}/                 (KV store)
    graph/
      amm/graph.db           (SQLite WAL, per-subgraph)
  zoo/
    ...
```

Each chain (and each subgraph) owns an isolated SQLite + KV store. WAL is
optionally streamed to S3 with PQ encryption when `REPLICATE_S3_ENDPOINT`
is set -- see `luxfi/indexer/daemon`.

## Local Build

```bash
# Sibling layout: ~/work/lux/{indexer,graph,explorer}
cd ~/work/lux/explorer
go build -o explorer .
```

`go.mod` uses local `replace` directives pointing at `../indexer` and
`../graph`. Production CI clones all three repos into the same layout
inside the Docker build.

## Endpoints (curl examples)

```bash
curl http://localhost:8090/health
curl http://localhost:8090/v1/indexer/cchain/blocks/latest
curl -X POST http://localhost:8090/v1/graph/cchain/amm/graphql \
  -H 'Content-Type: application/json' \
  -d '{"query":"{ swaps(first:5) { id amount0 amount1 } }"}'
```

## Related

- [`luxfi/indexer`](https://github.com/luxfi/indexer) -- chain indexer library
- [`luxfi/graph`](https://github.com/luxfi/graph) -- per-chain GraphQL library
- [`luxfi/explore`](https://github.com/luxfi/explore) -- Next.js SPA
ÎÀö»ûx»Dè# chains.example.yaml â€” full per-network customization for the unified Lux
# explorer. Mount as /etc/explorer/chains.yaml or pass --config=chains.yaml.
#
# Every chain gets its own SQLite file under {data_dir}/{slug}/ and its own
# /v1/indexer/{slug}/* + /v1/graph/{slug}/{subgraph}/* routes. Branding is
# per-chain; the SPA reads it at runtime via /envs.js so a deploy can
# rebrand by remounting this file + assets â€” no image rebuild.

data_dir: /data
http_addr: :8090

# Default branding the SPA uses when the request hostname does not match a
# chain-specific brand below. Per-chain `brand:` overrides win.
brand_default:
  name: Lux Explorer
  coin: LUX
  accent_color: "#5cf"
  icon_url: /icon.svg
  logo_url: /logo.svg

# Cross-explorer link list rendered in the SPA's network switcher.
networks:
  - label: Mainnet
    domain: explorer.lux.network
    chain_id: 96369
  - label: Testnet
    domain: testnet.explorer.lux.network
    chain_id: 96368
  - label: Devnet
    domain: devnet.explorer.lux.network
    chain_id: 96370

chains:
  # â”€â”€ Lux native chains â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  - slug: cchain
    name: Lux C-Chain
    chain_id: 96369
    type: evm
    rpc: ${LUX_CCHAIN_RPC:-https://api.lux.network/ext/bc/C/rpc}
    coin: LUX
    enabled: true
    default: true
    indexer: { poll_interval: 30s }
    graph:
      enabled: true
      subgraphs:
        - { name: amm,        schema: amm,        enabled: true }
        - { name: securities, schema: securities, enabled: false }
    brand:
      name: Lux C-Chain
      coin: LUX
      accent_color: "#5cf"

  - slug: pchain
    name: P-Chain
    type: linear
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/P
    coin: LUX
    enabled: true

  - slug: xchain
    name: X-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/X
    coin: LUX
    enabled: true

  - slug: achain
    name: A-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/A
    coin: LUX
    enabled: true

  - slug: bchain
    name: B-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/B
    coin: LUX
    enabled: true

  - slug: qchain
    name: Q-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/Q
    coin: LUX
    enabled: true

  - slug: tchain
    name: T-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/T
    coin: LUX
    enabled: true

  - slug: zchain
    name: Z-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/Z
    coin: LUX
    enabled: true

  - slug: kchain
    name: K-Chain
    type: dag
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/K
    coin: LUX
    enabled: true

  # â”€â”€ Lux subnet chains (EVM) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  - slug: zoo
    name: Zoo
    chain_id: 200200
    type: evm
    rpc: ${ZOO_RPC:-https://api.lux.network/ext/bc/Zoo/rpc}
    coin: ZOO
    enabled: true
    indexer: { poll_interval: 30s }
    graph:
      enabled: true
      subgraphs:
        - { name: amm, schema: amm, enabled: true }
    brand:
      name: Zoo
      coin: ZOO
      accent_color: "#7c3aed"

  - slug: hanzo
    name: Hanzo
    chain_id: 36963
    type: evm
    rpc: ${HANZO_RPC:-https://api.lux.network/ext/bc/Hanzo/rpc}
    coin: HANZO
    enabled: true
    indexer: { poll_interval: 30s }
    graph:
      enabled: true
      subgraphs:
        - { name: amm, schema: amm, enabled: true }
    brand:
      name: Hanzo
      coin: HANZO
      accent_color: "#0b1220"

  - slug: spc
    name: SPC
    chain_id: 36911
    type: evm
    rpc: ${SPC_RPC:-https://api.lux.network/ext/bc/SPC/rpc}
    coin: SPC
    enabled: true
    indexer: { poll_interval: 30s }

  - slug: pars
    name: Pars
    chain_id: 494949
    type: evm
    rpc: ${PARS_RPC:-https://api.lux.network/ext/bc/Pars/rpc}
    coin: PARS
    enabled: true
    indexer: { poll_interval: 30s }

  - slug: dex
    name: Lux DEX
    type: evm
    rpc: ${LUX_RPC:-https://api.lux.network}/ext/bc/D/rpc
    coin: LUX
    enabled: true
    indexer: { poll_interval: 15s }
    graph:
      enabled: true
      subgraphs:
        - { name: amm,  schema: amm,  enabled: true }
        - { name: clob, schema: all,  enabled: true }

  # â”€â”€ External chains (bridge / MPC counterparties) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  - slug: ethereum
    name: Ethereum
    chain_id: 1
    type: evm
    rpc: ${ETH_RPC:-https://eth.llamarpc.com}
    coin: ETH
    enabled: false
    graph:
      enabled: false
      subgraphs:
        - { name: uniswap-v3, schema: uniswap-v3, enabled: true }

  - slug: arbitrum
    name: Arbitrum One
    chain_id: 42161
    type: evm
    rpc: ${ARB_RPC:-https://arb1.arbitrum.io/rpc}
    coin: ETH
    enabled: false

  - slug: optimism
    name: Optimism
    chain_id: 10
    type: evm
    rpc: ${OP_RPC:-https://mainnet.optimism.io}
    coin: ETH
    enabled: false

  - slug: base
    name: Base
    chain_id: 8453
    type: evm
    rpc: ${BASE_RPC:-https://mainnet.base.org}
    coin: ETH
    enabled: false

  - slug: polygon
    name: Polygon
    chain_id: 137
    type: evm
    rpc: ${POLYGON_RPC:-https://polygon-rpc.com}
    coin: MATIC
    enabled: false

  - slug: bsc
    name: BNB Chain
    chain_id: 56
    type: evm
    rpc: ${BSC_RPC:-https://bsc-dataseed.binance.org}
    coin: BNB
    enabled: false

  - slug: avalanche
    name: Avalanche C-Chain
    chain_id: 43114
    type: evm
    rpc: ${AVAX_RPC:-https://api.avax.network/ext/bc/C/rpc}
    coin: AVAX
    enabled: false

  - slug: solana
    name: Solana
    type: solana
    rpc: ${SOL_RPC:-https://api.mainnet-beta.solana.com}
    coin: SOL
    enabled: false

  - slug: bitcoin
    name: Bitcoin
    type: bitcoin
    rpc: ${BTC_RPC:-}
    coin: BTC
    enabled: false

  - slug: cosmos
    name: Cosmos Hub
    type: cosmos
    rpc: ${ATOM_RPC:-https://cosmos-rpc.publicnode.com}
    coin: ATOM
    enabled: false
_Î—l¼xUQAnƒ0¼óŠUîà[K¢€RÔ"ŠÒ#rÌ6A1¶eJ_pj/«ÑÎxwfmÑ-GK µPÍŒ.}+šàJ:…Ès �âw4_­@
ÉŠŸ$3W?ppNË�¬È)48x�VÆ­|›—íë–ÎeãéA‰¾{8šŸ‡«¯°aŽQ2×ç¢"Âo¬•6Â‘uZ`ôÃ:A	:N–8‹Â3Fù§(‡Ö(Ù¡tKÄÊÓ¾NóäTdyßœÓ–Ât‰~Œ$ºoeîÄ¡už¦;rádOŒæ«›<­>‹ò=~ŠÖ~²«vu’•ñóoUuªwIRÆ>þ*?‡ú˜žÓcÜà¥¿ú¾™ö23ý@/ZZ§´Æ&þœkIü8Tð•ÄŠGä�%x„ {ÿÜ¿�timage: ghcr.io/luxfi/explorer:‘lžWDATA_DIR: /data
      HTTP_ADDR: :8090
      EXPLORER_CONFIG: /etc/explorer/chains.yaml“¤8)U)ý´�xœ•Wmo7þlýŠé¢9H½Õ*w)
ÔE>$vÚ3à¾(i?>™Ú¥´<s—’+E5üßo†/Ú+-Î@²"ç•Ïg8ËØ–CÅD=™ˆªQÚÂtr–l*›àGú_ó-ÿÒ$ü¹UÍÃ6õâÀ*™í^%“Ùd²XÀ…ª7bÂ€-9XÕÌ%ßq	('•ærÇÐjf…ªA*Vð6ZU�—hÜdNáÄ•«ÛÜÂãäì’Yv)4¸?ÜõÂß=É�'r¬
¡“ûÉÙ¿>~¼ySúëÜ¥µÍŠ!±¿Õ¬..ù†µÒ‚[Àˆ}M›«Â³�È{n÷J?Çt{–}‘:p÷…;`Py{ç–áˆ�ÛC€¼OÌZ57ÀÀà9$÷pA#[×s¿Ì[cU%þpèfð±ä¤‡F
£'…Ã¢*ÐÉ&�‚Öp
ÆÌ”¬á?ÁFpYXóƒB–²Ý.Þ³Š/Ž>]].>Ü\,>b�1¦9¨†ì2	ky€}Ék—ÞA4âcNº\ØYH	´*y|ïà]ôÉÄþþ¿FÕç‰ABâö=ŒnMáA§O
ÔHè¸uŒÐÕ%Dmø¢€s~%Šz�¢5

'­ÐQúVÜúðà|W¥P°m
ƒÊt
FIV³ÖÂæJÔ)‚b*e&gˆ3œÔ®›Ü+Úi�Þü¾„Óü{ØÿÞç$[ªµ’#vò¢ï¼[£À»š­%wWc­Pê÷„$
Ä5ÊÄy&¯R”é]­¥juÎOÛ’£cs©O¢x½K�2<õi�Åêª.øLöð]rkQŸ‰
…ßNñÞX^5öƒüœ€~ý¢YS¢q÷=ª:žhKÛ}UÑÍ1!dønXl¢*WmN©(Õ§Òâ¿?sf[�—ú˜‰n{ +¤ä˜KÏ+µãZ‹uT°:”l'0°QzT–Â5+é®ò�’òª¶\ïX—xÞ¥I+h	ø6ûîºÛ”m3H^½¤µ´LÛ·RåT…[‰ƒbC¤Õšh„eÈ¦nó
Ã ÇÖÅû×±üûL»v!6áìCÑîäÝý	W¡×,º+Ã;E÷!êÇÎOi®ãÆ‰3…+ÕÖ¶×P¨Ýyò’WZ¡&±Åî'·xtŸS‘}Z<FSsZd°ô‚Xè¹ÀºO©±n…´s‹xH™ÓŠ�J°>ÀùÚu–«j!Û/ìðz‹Å1»Ævñ– ü”UX8ÛZ˜=kæ»¦^YX¾ê‘¾Ç’ÊóV+8–‚MÉ±4Ø<›�KÖ†Ù_'ø+sÆ>Ë~û9BÔ…-¶’a®ûH¯­P¬<#f�hrÌ<¿ì•ÔXGÉ0NF_3Â;«KÇ)/TÑ|ºW+È	Sª}MhÿòæMW˜Ï‹kµU®ãé�ëáš³ð +„y |¤áNpÿsË�ÅN…¨ŸsÈY„7)­`�qT­%ušSÌ�a²·5Ó‡€®÷ü9´/1Ö>ja§å»ÖÉ»î:�“ç¼¶
ß§#yæ(«œH1¬ãª!8FŸ»!�²"€{©2'ÿ´ˆDÊI²òéÃõ‰Ã:+­–Iï°´÷IË¡£dõ´
gu¤‚öÆ*|Žzš²ZIƒ/>‘Óår)d”b[Jü‡a.©“„t©éò"lôVú¹	”®PÆ�Qd¥Tûk|ô>ÓÀˆ²’H:Æ5òŽz4u†“*\Ï¨XGÞ`ÅÑ@PÁm8–ÅãD!¢Ë„ÏÁªKôRF¨Âô€wŸ…XKdQY‡Õ5[sù<ž´ÛË ¿¦™¢AoÌ^¸ÝÔ°î½‹ã£xô†Þ#ùª+QÔ!n˜Å¦]cA [9žÉ×)¢:)¢ÎuÎÇY¤)YÝVùàïP°Ü @_Tœ×†m8D9¬±Þ`ÒL©;…‡	¶$×™fnquí`0®æcÒí˜xöüX›ýŠ³Ó…ª”˜Þÿç–Íÿx9ÿñ.|çw�/Ó^=}{ïç\êJa@¡‚iúÓkJ±Eã¾}÷þ·Õoo>�»40ý¾L�Wøˆ8©RºÙdÓÖyOçÔõ'�L/âÛVk¥gosÝÎ_ƒ2Ùô�*‰œMÎÄÆ¿yµ�$p¦9^�:ŒÔ�OØ+›½#…›iâjþs/öIê€rÊQÑÓäŒ Ë7Û zÔ�†Ý¨þ©®˜ÆÁQN½SCÞÙOÿ—ùð¯Ú§ç¥ sØ7¶œ\ÉÂ$MŠ»å­¸ËhZr€¼s˜>£¦Dü…[fÏ¤qvúº0Ån ‹8|ÓK¤ìW†wtéb6¥ÁÕ…í/pp9tû¢¸;Ç„q9âò^|FTD
'´¢Î'‡RPŒ,)�ŽWðôì’¸‹xœ½XÝŽÛº¾^?# ­œØrÐÞN|€dN¶M6Ázsr‘J¦le%Q©µÝœú}Â>I¿R²¼ëüœh°ˆEÎp83œùfÈJ&7r©D!³r0ÈŠJ×V„ƒ£ ÞZe|¨"Vþ(½ÈÊåä³Ñ%M¤…¥ŸLORæ,•�¬¬­è[óL%íj’f¹¢š0¶†¦™m™ƒá`0™c¥Í’³¹ÈŒ°+%xÏ…Zˆù»‘¸ÂÌ‰NnTM¢D¥«&—ÐÎ/›ˆ´Ö…�$(o6i6Q›*×µq“å«”$÷gíçê¦4ÏæE®™‹…ºRT¹LÔJçU“°¬\¨M´²ENz%º(2k¡”Ñb©§¬£�ùZn¡I“$J-L„uøë‘ó©Órp+ë��L�ÎælýY­K«JVõ­zè�©k³BA‡2Í–"œ¨òÖDŸÍp$$ÖUª¯´±$,®iF£¬c†%‘¹]ŽÄ$×KMŸÃH¼ì3ÕJ.œ™¹ºêVÕ[V«¿7ÊX²XÂGðêV$²f-+R³ëÌ®tcÁÉŽÅá²úqVÊzì¶R=ûlÝ$V|%éRð¿c¶hpT«eòV<>^!/ý­-s¦†üuÄ‡Bã�Ÿ(DwìÂµîv©•mêÒ@ã´�ZÁÜ\Õ"ÖF–¬ñ®$?$´£h5ˆiS&}‰!iëÅQÜÓp(ÂÇ-ãH¨ºÖõ�,$½y,¦3Ò}ÞÄa{ü#¸Ï`ƒRæz4e–ÓÊ#g YÀàè\‹M_Ú%ÎìÉºm‚]¨‰åbæ]Ï-tÂGC+~zn3›«ŸN]ÖÔÏ'nL’î­6¿o�üoLþu.›Šh2NVg*°ëÝˆtð§ôî· Âø<7b"BD7b¸g|ˆWŠÈ¢Ùøs	SÑù{è$† ŠÇ„<Ñœ2èM³á# E¯øäÏ°2~>½ê¶
F"�\XœbføîV�û9¦¾ÆÞªÜc�©ì“9Á{¨›h± Ã0mÎRØ¦ìÄ NÝ+ÀÉ’åVîrB’H½FV„Iž©ÒŽM¶P8­Æ"e‡}Ûé®;÷R™J—F}¨3«jN?Ï(Á>¯(@=ÊGWuV¼«UšmÂ:zù:zu­¤•˜ÍDp„¦ÑšÄž“ázØ¥Ç áþ.ü#
²èm¥Ê°:íß”µP)�€$FÇ¹6*�œ»“O¤9fÂÂý•Ï!:7'Y¿¿Ù.()[ÏæázD¹âL‰ŠÏý€ïû"9Ÿ·^G¯ 
Š`Ç¼ÚŽ¯�Ù¯Àªä"FW#pf�MÇ&ï?X+“•“„Zç´¸Ôã„æ3ï¦GÙ~°Rút˜{¯Pµ‘G%ÆpâS­Ñ©«ÂcfEB’0”GC¥•ë•´èIn�TÜÐY¬u}#’ÆX]dÿ@* *mL£#h‹�t5‹…OÛzE:‰�²�ÎHB(gYŠ@€¿þ›h+ îRy9;º+©O|�ÈyßÈrÇoIªÛ®shó^gº~…i$ýà œ†Žƒ¿_C‰¿Ìß^ DGð ‘
Y}t™÷	©O¡ür~uz}üêÅùÅ<˜¢f:9£Žtqzõáíå_‰Áé2ºpŽî±¼¼|qqÂ‹E§êˆc;Ö‹íH\ÓÖÔFop‚+™‡P‡´¢~'npÔÔHF/ò&5©¦9kk„”^G§¿ @Q°u!m0<°äÙßÊ€vø^üËªÊ³„ceòYÞJ“ÔYeÿëè'=^’1áp?¨"ô{·¶h/�6²ÉQô|!Ù5Zßˆ!ø[b(�xw>î
<ÊP¥‰]³7ô ,¾ø\qDÛ»¸£¢­tû¶Qùúžm]¡þÛHàÿÁ6Ú¦³­+Ë­mû"±Êè¼µÐÝ1¨n²94UH‹XXx É\¡õ~rU6°f#q¶WŸï7õ0¢*ù.\ Î”ÚÁõàhÀk*Ð|ï (mjÜyõ¾w~È³¨<Ôb'î¹pÄ]ëì&øâ{ø4ç>~OTÊÉaà ¹!3Ÿ¹ÉG]‰ÛBZÙZÝëc‰+ˆ2Ûòï¥xâ†4f:!Ãƒëî'xÕÄÀT¹£,ÌþôôiàWºl'My¢­æywƒFô›ñ¶¬·>|hÐo³§“3<´ôGMºoÑýöäBÛ3ºqfµÒ¯C½Õœ1P¾ê²�îÐmS‰sËqS™]™ƒa»WçPÚxÛ/c~CoåÏ§û�ÊG¹åªý‹£Œ¹¨%:+û„cŒ™ qƒ/í±Æô8z±3� ò}�ûµ=ÑÓ2� ¦O#ôq´»¾?�ò�;	^vN”Ým›Œ›Ò•1f�¡b¬ðpcòfé!ÉÉ ~¦£–šgBc®^gÆ=y,sócÔ×Ï¢çØî(œr¾I½¾^¿ÖkDb;ž£ÎÚ^‹8œ"ÿ8üøôˆ»F\Ó	BØRù>ãØ54’ÈíÔÏ|J…¬l”K<âjw{%�¿T¸“h�<	¢`HºÓuæ§�0'�ýF]2ûy§“NœŸü©íõZ½4 ~�Ó÷¨E¾íµ…»Š‘g·ª»�HT©­kEÝ=Î7Èÿþç¿|S&$W†ÛLŠâäb>‘‹RÍJ¯ES¡wE©È¤Uù6â—¤N³7� —´èMƒvÿà]â^Ã(>~z˜b=q^@ø°½(õ)ïËÜÓ¨ÛžÎˆúâ 8Ø>çÌ<k`©»Ó�î†6ô÷+Új&ÐËÑK£é~PR¸ôsç>âÙ
$ È¤ó‰ÄóG¨«Ä§p]¾;öÜ`$ómëÜÑè¥¥h»YŸk,ßÇîöÞSÈ>Èö@Ÿ¬ã·+ÂO\àTiè®ÃoVýj1jSšŸÜ¸uè÷»:±
—|‹›Wá“þ@±ì2½m pÆe*Y=Hõö7:ÝX_g9n$`&àÞjº;â€¯ZÌ>ÙyÐ±Uå!6šíX>WôxB¿ê/OwÌkW˜xºcnàÙŒ	ÏŽròbŸãk~vpžÂ9ª®à£~uD¶ÖˆO¤8ÝJÑ¿ÂÒŒ^p®{ 7}\jŠG¢1*møéFðÅcãªKG‚Ò4)õ’€YÖ¶©ü�ö”ûGø¿?=OÙV€0Ý•!åéàèZ°°2¿¡Çÿð¾¿î:ÈkbËiIØ‡¡åÞ…Å>y²÷Ô9àüh¹°(*dXîÉkö»7HXI=Ê âH	‡±ùxœ•WÝ’³6½nž"—ítÀü¸h_¥cŒ oŒÍÚ&›ìÓW6Ù-ù`›L2dr$ë÷HT3	8vÜöS251][Nà:
¥A�:Æar
ãÃAÃûÄ5?ü¶è”æBPòµQìöx‰Ã<Ì‚(L¢$�Ò¸ŠÓ¤ŠŠ ¢SÖ¦	¤§"{P1ŸÙi:öÇKÆaôâo.¸‚v |QÅþí_A´x½i^©¨µ]Ì»àÒã%AíéŸ\â¿#µ¼€j<w!—äF^•"*>üñ“–>Ž*äŠôãÜqY	9¢éˆaöáè”–AŸ�*\Å2�g*,'5egÕ¶ÎÌ$ŒÌÜ6#Õ@®×žšž\'�nXÖøLè‡æÆj°VÝ¥¶üi&c¹�e?TòOØó
¡¬çÄ}.ùñ’£KùX¨N÷ågaº‹5¶ñXÖVuH+¨­§¶�ÉHò0ÆðV?‹ï]Ã4ñÆvÚˆÑW¯ÓÕAËÍàÌP=²Ù…¨¦²aÔ’OÐŠ)ÙÎ�_7£§µ˜Pyï³Åß4Ÿ%Ö£ô ñ6	Ž'Ò›Ù?H~*Ê‰†Qp´Ð7ÅV€\…r¦ôˆç*»@èé^{QX¬Š½QA©àÞ¸ùñ‹&�‚â¨Dš8eqže”'9”eSååª¶³ “•±Äe_ƒñ~ÆåFu
^“ñÝÃ¶2%n-6‡$Ø*ž(ã�2ž©‹v0×ûzÔf £¬‡½Z¹C¿ýrQÚpëŽ–lÒœÅ¿g1Ó·Ñ*ï]f{ÚjiM|á³<oÌìdµD
˜·ý¬rïl‡$5ïî£dVs6�Ã]ptÎZ÷�ŸttÀý$#°©É%;^²°ÚHÚ€†³üƒÏÂV­TŸ'3¨šOÆõ¦p¼Ð{!üžµLánA¥Îÿ@„|ºÍå—lÇáÜ?ë]kë„6èÀj%‰«<¤oú†ÞIJÐŸh	�¬’ÐýG/¸Ý¸%¤L³øÐ¢ae}ŠY™¬-™žp&û­ÃêÖòöxtä€ýÇˆøÌî	E{×OµÂÚë3ÄÇ¾ýgfSïfºAóÏ‚ƒj@Ì¬ý315Jz�bƒXøÈÚy9«¶Î0T¶J7ä¾©]Z³àø<Ž“".â*Í‚Ò¢”VULWÕinjj�{��ëé¶µ…iãÎZ%EVIÖdôÔ–¶ªÐL¸ÉµâF„½:=[-o‘°m­ù njïÑö5¸]Ö¾h4$þ¼Á©P�˜3à˜çæQW®Ä4ç×ú_(ëÒ�¬ùÜ+èÍý! ƒU™©*Ý}ÓâóÎ B¿Œß—òïýôWšóuïU]¿çîåÕ³¾o?‹R8á&‰ó4IOAAë8Ë°êjKv�S�>é%ÁW\þ"”¹Iæ	ÿ™ a¾^…s�²põg¦ù6L)1«{QenÛpl`«pwöE‰Å§*»_ž$–|‹u�óiÀ¿á]&œóöTšŽv´d^?Ö½¯®â) K”Í“å¾x‹-uJßü€xáKÜÌ3¾DË_pþÞ7
WÁ�wáåEö¯¿�a8?oÜfgÔý*þ7~	ï¼ñxœ´½×’ãH²-ú~¿b¿Ãª Õ5»PB� �—cÐZ+_™ÌìfvvWMÏ>Ö–²¬×r�ððp÷ðˆ‘±ûÙ)Èq¦úiü?ôóñßBp�(Æ1úÓ‰ùdIô?üÿzÊxÖ	'òðÝŸlÌJìn [fUDÃÆ80­ažUÌðQõÿûÂ@¦íÏº}r]‡#‡pä€‹%_R ìÝÝÀ1* ·KélòÏ·K�s}Ü>¸ªvŽ~¦m›VñÏ°­8ol0ÿ„ÞÀÜ£rÒæîÊÃ§¾àƒ
(vt¡à´ä@Þ<�ÐPHšKªþ=Ø‹€P�uw:Ãæ¦ë÷ââùÞBJcq¸èèš¢zT«¹fXßb‚�/Ý<Å`O~äOþ	ý.oÇ[vQÛs\ô=E'èœ"¶‰ô.•®±Óª´By¤½ñ?Â~_€g½á«Ù(ç8âN}‰vrm§{U‰þ"Á›ÉFB€r0ô=Eî×ÿ³À?áŸø\˜#7cA8j£€åy¯™øõ¸¼Á%F-ù¾o2c‰MYúÜ‹�KœA)Dë­<‡¼––Ä 4Š*]Z'q;9SoB°/Ô÷¨ãÔOÓƒ¢ÄûØž¡Z/™Ð—ïg.L©fw5ÌS®…s€¹u}f§*c6sø=ÌqUBQäÑS·«¦¼�u<ZDƒ\süù7W›¹§úø€Nò*ïºögÞ‚YW>Wö1û8‰Ûö…˜âë Ÿ‚l0AzÓ“LÚÓ::qe„Ì_Á¼NtÍhFƒg´�âpæÔ’ïøšBã¨â!Ÿw¬LÝƒ‹b»ÿßÿ“æS6OM™}bÐûücŒÊI;üxÓ>*¿Ûá9 0õy#9øÁ|ÈA^ˆl‘ÃÒÚú�>¹qD9Uª»÷\ñ8óÏH^ºlöx½j¶à¦XM]vR.Èx¹Ç˜Î^RH†ã“Úl“¤ÂïsåQÜLù´=ù>¼†š‰„SÇ ºíÜä§CI|)e�ã3‹Ž44�ŽoÀ'ëŸ½(Ö–àYFÀZTÉ@7ÝÚí¨Íº`îzëë
ÓCMIˆ£üö æÍ�_}°=çŠÎã„«*ŸÌÝ.q�h?sßîQÝD…YSü-ê¸ýìjú�i^”ºMJÊ£�b<ãÛ’Äl"ÚE…ó[£¢Ü\Ô0`5Ù¥ßeûX€�ª6xã$Þ5;¬•%ÛZjù ÏUX)Ùç(3…’_q—¬cãæüw\/ê-ÎD�1H­	Wò!rXo�°š…�°)ÞÚü^Ÿx“‹Ê?S2<XçáÐŽm2ýðç){3�ÐŸò¶ùQåÁàÛ‡,oÄØ»’í–êPÕ†©'Çä¹sÏ7gÚ+WÙ?÷áâ“¬Çµ§¿íÿ5ã‹ªë™BÏ¸Þ^ðŽÈ;ôžˆezˆ]æ3³øa'Ì×}kçžù<ºç¡-âpòN*èåCÜ€áàOñø@þ1Ìú:÷r©ÿÏÇ/a!0¡0Ã?|È  �)Ÿ‚ß¤1Ù=®nlÔìü¹�U\B“už–Ž.Á`-�¹™1õÿ²4/C”·ŠH XWncñÊßu=0<cÌj»Mw½ª ^ÊÏCäWù67o_?ðŸ›Î�vÓð´Çyn6ï^i½T};u[ážy´6Ãc&¯M½E%u=q¸GÌ0g �û�8^Ô9˜ûÅ»6ì A<qs,]=…eq‹3,Rµ�ïÀ3ôç1ö×ñíã‰û€‡Æ>¼ê¤-}�À-7bà{ùº|\<46Ÿ»™Úsz>EØï�¾nÀþ¶),µÁÌÓ™×—œƒ–7˜9õéâÝí>¥,0‡l‡þûù›nh§6l+0^Ëdœ†ø=F!bodE—ƒp ï’Xê¾C9dcdvŠü6”Ü‘H­xÈZÿÙ‹‚’ÎœDkÒ'ncÍ\WÕŽ”ê�òK½ˆùå,hÎ½ûÅà="Ä&ÉÓçœ#?‰7ìì °¢u*j…‰Ë%‡ãEÉñÌ^—éÚ¢¢»æ {ÇªNN¨ÿûEü*´0#ÊÙ
nP›{¼ãú­ÔQpXC­^’¦%�©¹i¡ô+Š!~n¤~5>7ú]‡½8~‹˜ka#ê–oöˆæÝ‡4É˜˜±JK…“�=_øÏ	^³“ô¸Ÿ
Á£Ë@è];@y–:SéD‰ÁHëÝÝ¯2¥S¸}tÁ“Äþô¶ûÄ!æu4~<ðS
fÁ”œ+ÌöE£Ñ³E¬åfœáÛzØðe=®'| ËJóêàu}tç»;³ y¨{ÚcÖ5ÂâjL—Žq Œ;““d)êÚþ&Ûˆ‚µß<öÏá�y$QÔ3YœÛqòãnm™
´æÓ~_S½•ùÛ™ãÐlI55	ëèŠ˜¿«Ö7D/Š�§Ùò uµÌYÜÅ£ºt‡!eICù„ºØPgê²q¹þŠï�@çÃÄÇvÂx|ß+ß§lHÅõ¼’³îu9ªÇ[Hetm´ºÅz’xHOÝ7¹ïyáÅõ¢žZ÷QŒO�ØJÁß“LwŽÎ,ÅSal7¡€€´�˜ß¥Œ›¨k?�à›ŸE^è];¸˜Ä}¶É�BîM¯ÛÉ9@÷ZõGIL!5¢˜E‡¸¿òyKõêÒ×/	pSbàê‰uÝù²m€Œ"„HÀ±s{¿2Úo�gÞäo£H½»qç4û÷ n'§::3-p�Ze>åè‚XCº%x}��@1L~Û8þMð¢ˆçldA,ðâˆõÙˆg(¿Š‹‘çcŒ¹~-T?žL�pæßVdÁüWÓã
u·F’2x~Á}FåFn@)…ÂãIÐDø›¢]\äÿ„áE•Ùv0]aû@¾]�²ËÐãbÒl5_—eöG6Æ	a�wýÑKÆÿ&ôÃ0î¦q¶QÞ<÷}Ÿ(hØLMˆä`·8Á)í›pkûêf“ˆŒ—1‹‹¤¨úÌCÐ¯|ûï²¾¨|¼`ýªyŠp(ÇÆ:‘ËÊ¥J'uïc&÷œa@ôÄË‡výOÉÃ,Ëq~îöôOò��—Ø^d2[€ãj©Dv´ÛN&€²Do«©1úë17Ð~ü•'þÛk*£ÔÐc-³eLâVÝi‹zð‚BÞ¡Â=V½xÇç¡ÿcÒnˆÇ<mâèÇ<T£ûn³í±��±µAR-ÙP07èFÃbiã�Ìl\ÁGW�™¿Š~“ô5Z7®š˜ên�»ë•»ÝoþÃ7](0hÍSCuæ]­ÜÒøExûgî3ÿA|„ïºj×`¹Â¤ZIH5nzk8]Y¸Ã¥FÖÁâÜ(Ëtü•óü5ßk¬²š”Y÷62„÷¾í'k4XõY<&Æºö¨ˆ_àWÛÃ´#ú´ü£L"9ƒ¨×G¹ƒ»1ÛÊ"}J»‹íåIY‹ V |yIÿ1þ‹$}EØ¨çäîÞ<Ù€°óæ6tÆB,dIÛn�>&8oî¿
OÿEó°Ž¼y£‚Þ=Ì±“H_­e ®áµ¼%™pÁ?\8¸·øì PÒ\þ	Å‹2nD¢êŠU¢B©ÛƒSžôiƒ	Áv£iÆÏˆ�€}.¿k
ãøLÙQèç3ÄòUð‚`ƒT]ÑÀ`2s;ÌC»Z]ÕMä'AwÁ‡Ù€°õ»þê…àE q&Ä:^¬¼’çù‘z±*»ÈHpî v„²]['^3Ì!ý}ž6�Â'þ~Ö<˜#?@¼ÿ˜�–
é	Žëag*›?t@®ª§æ¹qÓ1•û]×ÿ•åE%ñB¢7=rç¾97õè$YÞÖË�nS¤ù˜äÔSãý·íy?’Õg­üæâÁÖ<¬>F5™v<¥žÛp³Ý‰qu0SÀx}#ÿÁ«s0è†]ØÚºGg ñ4½Kê2R½Ç¶±Ä{ÀÄ:`ù‚tŽlÆúñóöQB>
ÓJ—WWKš´óa€c—Â75¥�%á<-‡`,ýCX×’Õüàk +ˆ¢¢k\äûÙ3Yì4ï^é?Å;7K*eÖÍYñ n‡†»x(Ç÷5ö,ÙUp:5ZbS�¼Ç}ëc¡~êu"pÔIOÞ]§<˜ ²QW÷jˆ7ØŽ…8�cÒ—‰�Ùœ‚faÁ¸Ê(2™ª²û¼¢Â¸)ýjÊÁÀË6IÞb\ä'äÍÛ!�?åA¿×EÚÕ¤é‰´´¤Rî‰ë^«“;²Ý-â	BÏ)’ÂÄumWì?ÆQƒ†ŒØX	1ê¶d8ú¥](àåè·›�ù¼eÛåí"Ë2ªÝnêš±{ì0àýžùcö«”¤,…‚ñ»E_ûñPåÁ>§é^Ü²6%…ho4h^£ ÖaóE\[¼Aü½¥““LËÄ?e(X ìaÒ=¿)1‡‚Ó?CGþ‡iöVÖ»x}?ZB~@�Ü¦ 
…`†QêGD%$Mø	Š†áSº?_%äüÈ¹¯°½¶·‹I3¨³Ñªú°�¥¿ÞNÜôr4«ÿ5ß‹š2éRnè Zž�im·`½ ³><t©kX®‹ë(!~D?onQüV§ £px~ŠÃ‡K;'Jø»ÿÏ‚ýq¦¤©^.Ô¥9Å°’Žïo*fµÇ5XVÞX	ÍVÍgŽFþç¯YÎMsïcV­]7pÐ®Yáîj¶	¤øm2¹'¨4Ã>/å(ü.û‘·à�¿¿¦©ý°‡$p…èM)²(´yäbÃ4bÉaOEgÖgä€îôYÝ´ºUÖï¿çž±çt[šúfßÚµª[f©�m›qõ‘ÊäNH*¶´ðËümâ¨_êÅD#Øcò!ú�b0ádŒ‘‘ÿÜž¥djR‚“2P}FD`Î÷0§3ùÌR­Ú”áÍ½Ã×ùþ�Ù^ÁÞô³ÕŒ‡"©o†ª�· ¼æ"Ä¦–ƒ0óáH�™æŠäægÍã”7oœÙ\ûM¾Ç/ÎVÜËÌ%MÜÖ2¡\�ÐÙ–Ób’<dì`��a¢5_v1çþèkî?Ã»4·Õž`*ÁzÜ-Ôó0ÕoáåÔ‡­Ìð„­¦ÏIWù=�Álšº±iÛî%ÆÓ¦�x(XÒcï0Ü-¸l–MÏKßQ p½"Nú[˜/âÖÔé,ŸúN�¸Ló©á"ÌµB*‡KÄÓ-¿_×x‰}j?"ŸMô1a–ƒoþ?þpßÏ8AH±z;zl±-rÍŠÃâ¤CÏ›·Ã¦ïEAºR�*h=VúK¼×9R¼cõû‘‰Æ¼®šãm¯%V«ÓWö¨tL2wûA>C_a«6À·OÏMüÓéP]ØíÌãYz&{]ÅWš-d;öˆ’õÓs+XcØ;3‹ŠýDßk_{*/BÀh¤'âSÐŠ:™ÇûúR9)¥òH�~÷ºù™0„‰û6˜yz�j�ðšÂé¨æ5¤ÈQ‡ú‚A§Åýuœ¢?TVÌ¯ÎýØöfê W¤CH1	'Ð“‡»'×ÈLT=œ<5(ü?MÐW¸W3Rjbu<rƒ83„6~Ä¶Öiãë\·Ä|g1fŸ„/¨•ß¤?Šuß>Þ§}_¥¥›P0o:‡˜œ Ø^2¾Åé`;¾Õœè�…û`mÔå¾*þ=æ‹¸É�3GÕ1´2BªÉŽŽK(bo„	 §‡ÆÞ,ÁÇƒïÄÓ¡�»Ð³×£6BQA0Ç`pÀT�Äô»ël%üÎõ#×âVQ¹füÊâqw¯G¿g�!¨Æ‹Éíÿ{¾5C)Åâa6‡Ì)E%»q³p=Ò_h)¼�Œv—<†ßÑ>�w‚9y›nüÝÄO�Mñd[)i�¹ctíÛÃ«n„—-‰ðÀÎhk%ÂuýÕz¾C|]àö%BmmU¹çÑVD¬É;,ÑU«\oÑ��òYƒc¬¿Nö[›˜TþôÀMâá#oÉÑO˜þS¸šùª�È†!s1LÓÔ1vÀºÑ–¯š'âÊ·P’(ð�2þ3’…`&¶çÓ˜9¶0FëùÓNÔÕ’ï–q|,VØµX>ÉœO}ÇõæëîmºñO‡8™‘¨"RJ!¸ÈK$n#H”· ]¢HM0ÕØûWKúK~œÌ–J!‚9.˜ìZÞ*�ávnîäìè*W97Ç�Ý{Ö~!&ù)2ény_7W†Fô£ øôñ¦ &�UBË;W‹¶¥ÅÕÀüë¶ò„í†’|:ÆF1üp„ÐÆ“$ :z�~m68øpÅ»ŽÛøÁ;YùÃ‘™�}ç:zZ­ºö{:¨ñ¶þ7T¯[ü½KöZdµ¤Åè–&:¨ø.‹ÕOŠdp‘.j·>ÿêçŸŒ#âÿøhÏ{¯œ«\7sÐíG~æs¼ƒíœÎž4µ×žJÚ#w˜¦`[°ý%Þ‹˜8Ä¥VÂ…V9ìŒYÉcÂ$³cx ºÈ;o©ŒÔºDßÌ<çÑ{ïÈ{0.-¾Ì«úXª�&S®é>0ûÓÈ´!xDo �¾îÀ_Ñ^D4¥íìÅ˜šöK»<{ˆ#µÅNXA¥×c_7�&»[ÀvüNs¿ËG0~+'v�89þÆÃ”'o­!ñ�Ç¬Þ··1Aß·=;�ñ-X„Å$q£Éû,9ÜÏÆ†²	¹h…q˜±4tó/ß-ýßezM•k~½²#XŽg…*{TgõáriüÃÖD0Ï� öwãõ$Lýû[KÏ{äÿp6Ïy`€”“O‘‘ô·r XÉ'OÉí–@5œÐvq"ž�ÿ—Šü	÷Eìð¥~�ð8€]Qµåd¶Q˜ÂÍäÉþÔ�Bñ8 ñëÂòªòÁz¾¿#½7¼Ì¬‚ÑÓmM‡ƒëdý ¥ƒ¥	.�Ì|=g39šÑ~rç«WÿŠö""sJh	c„þlÖê6…j�J­�9‚°Qvò°žÇ“Ú¹æêå[Ð¡ß˜§ˆ(ª�Çã4¥�½hº¡€÷V,¦ùˆ PÈò2—äéËqõŸÑ^›´NÏ3¦:àwã Ì¦›%´%œYCŽy¹^¦©T¯Ýd$â÷ kŒmXÆÓûF‰=srä­‘‡†Q„†È1D`	ŠÄ(A>S9¾
DWŒ-nïÌÙ]?<ö�Ø½{ÛÔT–Ó§œ‹-þº’þ	ãk»+¶’Ð½v¸Ë©Îa];b'Ég»)Ê—Ù8oíÈÒF}®DaýC~=¸ÇCûvZüž›<m|>f~¦°§~uX›p<‹gLòá‘±eÕMPCªYyi‰Rø-Ì×nóT‹ðžQÄmãZ+–ŽW;È*»rr~©Å$<#çhß&áóòÉü šõ¬–dy1äïØOç&¨zÝÕ“/·–,’)Á“)c†kúê4úä%`ÔÔQþè‹Àúï7WáÝ¸í'L‡ýè°ÒÜI2=T#æ”V|š÷Ô¹ü5v1Wùchüm|e†rUr•
˜¸îÕÉ„®†Ò�Ðyî[{Ç*{†gâô ¿–'dÝí±ö Ï|ám;vsHÀn{fMhH�˜�®°{$†_š½õspˆ»êécß¼ëÇ6äÞikªÍOS—b|iÒ˜Å�à±ýžÎçÓèIm�Ç~óEÜ3»µÞX9.L‡wî7g™¬µ0q� :“#›î®83uü:c–‡íÐý‘yTÃüá\¡÷Þ‚ñ‚‹¨ûq^Wb²ïí~övšÝü£¾F¬šë„|µ�¿C~m/ŽÝvMÍKñ·ˆód]ÛC¶%*zq×2"aˆ]ü¹˜µU^û¿�7‚ïÝtO“öi!UÏWf¿€¯ž‚ïÒ…G�`5çã Šz<(…X« Éüâkk·®^¬'´m¢ÏDœÌ±�Åp€Fbeq·87l¬IÉÏXø•ïWÏ*ÒÇ·Ÿr!
‚þ	Æ±„#xLQ�?O•ú»ÒæÇ„7{/’eN7é&ëÌxa	D¹cè¾0Œöþ¿@ø¢¨Ó'3YÖ± ~¿^Ï~JØ°zì+�²Ä†uþØiÌ/-ßeåÏc×ŽÓóBÄ�ô==ë+`öV©ábÀv4åêôç]·âÆêÀ"œ&.Eë‹ìÁA¤ßE}Í×sJágý�„§œñRP`6eºvtù6½—Ãõ³¯)0ßÐç"•Ç2šé<Ö·)îc)Á¥ƒçJ:y6¬0‡ôONÖyiEý%ÎkKç¡ñÄ~b5Z¼dìÀ³‡!Î†¡y†–™ÐÿÐrÈq5Ò¯pÝ[ñô#:{n÷Ieð˜¦­.cue—Ù!|§ÌÀN‚t�	¼¡Ôï#£Aáo±^ÄËZaÌ\ûèßÕ·uáŒ{c˜'3ßVÃz>y~#¢÷ò+äß§7À�Ø×î�ëC^ï"[j£qÙ§y8J0*÷4Ît¡íËlÖ]Æý¤Ñb%H1ã2gµÌ#õÎ��
aÕ#Yü£eÕìJUH:|Ñv«â*®Ûæ­Ç8Šƒù½½ä]Jã¬�¼Vâ%BDRÃ¡…c^9Sõpè× fè>7W6�®_º›þôµØBƒ!ÞµcØvx�MÎ¦ÅB¢_)YrJœ‚€DëýÔæsÎRåØõO¼�¸î7A¢(%*}ÌËà�É%Å¯ªŒû9PvÁ›Áî6­öï�^D;€Ò®�®;#Häš«·C•n‘¹†©9 úõ¤Ž<ÂæS÷Ù#Vu>M—Ø€ÓÃ'¾«ü¬¥Lð�Îí2 ¢yht³O	e-Ìä¥dÏ'¡õPé¨ôkÀ×¶éŠ¦×�ìµUä
 o�‚™'a8°öØl/g"”=HKÔõó¤Wó=ÉÁ�[B'“…÷n<BÖÈ·{ó@Þ¶²ü|Q[õ|1‰nÀ`e\Dˆ¹ü-Öë	/ã2÷tn÷p#1Ð—Œ‡ŸôËŠr×«~£×�¾�ü¨‚ý+|/™ý!á`V»±v2 èÈœ¬ö1Ê%ä¢”(ë÷‘G‰ô´W?Ø’²y÷¼cw2QØÍ X9t¸ïé÷ºµR'«\¢ËÑ¶¾ý—ƒ}Û.ž^›ë¸âê	fÜÒ‹Äå&L?©ù‘Ü¶RJkñl0 ›È¦úkÀ×–ŒÞî<Ò%û°t:Š8ˆÈ¯Ê)I n«ŽÂ²{rj)ã*}/hÎÃ7Ó;òÓ*cy[à$ó9¨Pu%€Ë•$±Ðª;R<í¬¦‹ÂõG†áï@¾æmx0píá~B9cÚ„ÓN‘MØ€b9Øñ�}-ÖÏÖï‘ÇÉo>šÞeÂÞÒ©iUõìèp©A‘8seŸƒÅº„w¾Gß¸;–þâk�¢•2ŒÀaÍåz9óùé„]²ÆlzFÉó#Žê‚!²C¦þ‹A˜ü¼yëë…þ8ÂaoÙ�Bœ-éí3^•ät†a³TÓÁC™ÏÀÐã·ù{øŒøzx‰^»Z=à6‘¤ªBÍÑ½YF× ‚jûñü˜Ê QVôãò;àaë¦g{LþÄž¶:‹'hÃ _É;T!WºàY8:ä‰�€…çB};�±ò�ûøøÚ‹Ð0ev!�ÚˆÞ s¿ã�®kW´¬ Í¨�cÀ‡†Ž'‰¤ŒßMÖÛ…ÐÀã?�Ÿƒš%¹Y�äØkì -–û^ÏRgš&;Ÿ–.uêL#\•¿s%‚|–6|C%èdƒ»TIÑ¡·æGü–”0©ÉP‡ÔH/Ôþ�°i<eOTâ'A?×éQ'î¦0 šO%�M#6o¶!û…fŸQãî˜…Üá½¨WßMÒ'¸!©XÐáÌÁGŸÐ�ˆsªÏ�cG(™½îÌrÎY-$ü5o¨G~>%¶"ÊnMGC!^õ×ÀŽ*QA„ƒI¾`mzôWë;?úo¬ñÊLÖ…H?ß#Ù¨í¡	n0Ãû�xlp—ÄÒ…Û5œu¿[DU›~tñ>«Rr0Ú( ‹�Wòäo¾UBz<V£¹D'APe®0¨ô;ßño¬×)Æ$½ªvåemÎV"¯hÇäjA¥q%Y�^UÒ ‘â»Ñ«ý÷9AÞ—wnïä¥fËi–"¡bR©åSª²‡�ktXÁÖñ��OÑí`¯%ÀÔ´»ÆžHÜs(õ,È�»‰syÓr$kž¾žîwÏýL0x»Ñò¯°ÜÞ±2FÏ>G�\y<žSFæ²#h Œ;GÃÚI¥T@“¾ÛÌ¿ ¾ÊÎ÷ëŒ F Óè�UJ\ðk¼©¢C×6­L¢wþ°c³Œço•�š×ÜÁfÑú
fHøÕ…¯DÅ¤‡ip(œ™OÏns¿úÉQ¿òß`¯¡eñÉ.¬Ùò+Ø†GXâ÷X¹éR”�=
ƒ$òºåßbÆÓ�‡ïu±§1>Òw¸©�f ·}Ž¨ƒûHò¦›˜U…%PzšR…	¿[+Ÿà^Ë%�ñ˜ÜŠóvš«ìÏš[)}?r¬ÙÞ!r@ü¶01È|‡Ú†å»âO¡£0IE
Ëçì€dmgbÅ^sÌ°¾‡æz‘2ŸŸcýÖvþözZÞ¢¸_V¨=E”ìü4R¶õÕÞÂ!‚Á˜Í‰”˜ðìô]°1úuW½_uù¨?•Ô5!ÚÉ¡Ðë.TÜHQ,Z–\Ék»ŒN­™ºý�÷þŒ÷z¶K`»W½¯ üR™†cƒÙÝ\]üleõrÉºXŠËï¬qžò�ËSï!ÆÆó,AM)~×÷}‰oÌxÕ2ñµ;jÆàÂ~láwB¾¢½!Ç2Ì¼Üuö0^77E5G9¯ÏÆv—F\™}H®ó�ˆ»ß½gzÏ™Ægõ0R›Áƒ*½Ÿ<Œ… òøIƒˆ
ÒCaðø�gü7ÖkTÙ›�ª¦½‡¯ZÚÂJ\¸ûÁö–ETá4rø({Ówöý€Œ‚�>+úãÔØ†ù!ðúÉ½ep¶óŠóÔÃá@ü}ï¤8ý�hƒ{Ú~ü||u<C§ø|ìnÀXëÆ\ù3w89Ã§¸[¶¡Ž&œÅÆ'—ù³EÖ�Ä$É«øùÍsz>ÒâÂßÐ”Øaw‘±¶:g&¼–£^}Ð†|˜:9 Ñ„-¶ö×€/‚î¸å`~EÅV¿6�œ:s¾’Ñ½šÛ5g ,Ž·þ’RÔ—F“ÚÊy¬Û ŸŸ·}!KÜùÃøqeì}ïAbê&·»/ö]\G#%‘°ÍqXAüÜí$�Ë2RB
ÿüüÕ$XÆbxyVµLî6•�\ßaÚC6�ü¾±Â½q{™jsê~á˜¦gÃTØVíà¿�Ã¿û•§ä4C+Çk Éh³í‘B®5Ý‰hy¦ÓëÎÒùxý/Å¬¿F}Ýß•ú"Rå c"¾Ãéyq…½²RYÀ8¥gï[=[Ã#õ=x>úõž·³�gM!á¡œŸ…SÃ?ÂV´Çª¼f�åˆwE˜°°»-_Gø‹QøùZ,lê\M¦©êËéáãå#)Ísx¥±+ÂEry229³Eÿyì«|ŠÑÛ{­Cj›“rà¦ÍbA^è¡soñGá\…ƒÆ€§‹‘”Dðu‘|…~«:£jªHoÇ1Ùºsé«Þp
x�è‹M�eÊ§b3p‡G�îåÿ9þkð¼§~çy‹si4q”›eµ(j/ï­äŽz_]Ž_ªNu—)øží­¯eó“fñÖ<® o�÷Ít=nû…ËÃ`ÍÒ@K÷‰ÌYóÈ»úw˜ä³b¾dõ ˜„A§¹µRá'zÏQâ€(W ;™†Y	U»ô÷`/Â,œ³è¨��ùqJÜ4nG/êËú�%Ô øöc‡2[éóiU=ü˜†¶ßÒœz_ÎO6Á#/³æqçÀBiÑÁXh>±—ÊBHà"QŠŒMÖþðÕç�‚ã¥™*I€“áR‡³ÕžØ+Ê6R6*	©µõËìÌÍ#«ÝHþ<µMœþ»ÆÓ0C…b0ñÃ'£�
8¤�ç`sèJŸ{I‚`Pïán,¦¦(U?†Ž®ðZª®*Óyêpdþk¾×ùhH“ªOÔq¾˜áaâ½©A^º«znÐ›Iœ–LŠ óËç@²ñ§ñ­�÷íëÏ�w*>¶�;Ù¯DXògN¡”¹ P#§nè½®ƒ±ÌlLsDÔß�|ÍG:·¥-;/î³zé±�ž&Ÿ5Õº¢ÊKT>P4ý¹Œ·ñýmøjôÖ‹öcˆJ7¢8€�-µ}iœûd\/$C±moaâC¿|tÜù¼žÒ:ÑsºAp¹K¤H\adòî­ÏÄ{‘Ù÷‡ç<ÚÂ÷¸½%Ý½xÎP¼IÙdO9'D¸†Òù‘æƒƒùø>EÍyÅLîñËõWp¯ý\ôžwôœ¡=$[Þ�TÆgW$Ÿäó=V X¿êòÙÂ›p˜Ã÷·:ÆiHÞßçøW|TU+œyîv§‚^Ã`½Ÿ�^5fÔ¹WpŠ~`VrýMÔW—¹†x6y¢m—cœWM’êv@×§�XáÆ—ë†æâìžÂê³÷èòxxëš¯vì#ìz,÷ç²O‡•˜eþ€i²âlÈ+�¬à>;2‡h”L˜Š�I)HûK½ñ{ÄQSÏá;Ð‹AI¾‰·A«iB„[^÷µiÝ„³qÙØüËÝÃuC»Ž”Æ>úÊaè±!0
ýÀý
ƒ1øÙ.ÔQQŠ§èxQÂ·ŽØBbCãs•-àtz*¾üL/ª‘Ãª`œË*M¡Ü­^’µ†sr!Ð !$ËÍa<Ðª&à8ëO„�Qk‡ñý!´§mV¹Ãû�±víŠ�ìG6Îï�e”svaÖá„[«"šëôçqúö"`°úë=9²Í$³:øá5ù9±QI�ÞˆxÀ5I*èO˜c2u×TŸ×Eåƒgb·@·ÈŠ$ïâµÓ#èVIíÏ†I[q»qN‡öo±^Ä›vxØ4Ë8Í$i(€8s{ÝÊîýî
º*šÖ�?�Rìò9¼íêx÷‡èÙæIRåÁÇ¢þ¸à#C8†!?ðC),Ž±$xš¹ì×KŠw£!…áHDÍ¬$C-MjM§�¹[ÄV}æŠÊ—þ!å«Ï>I²|ÖîÝ<Á]h4å©ì³MÇ %SO³]l4x×¿ÌïÐÖñ”Å�H>¬ò¸™þÏ{ÇÀs§þ8Ò—cú‘d…nðö†xµ‰Zk,˜LÄ…(Çè2dÇÈÁjÿ#ì×~¾ ö	Å9²(x=K;×úüMÂ$Ê:žâƒ)\`— HÜú/íÁ¦x ÆÕ{�ÆûRì˜®�8’¹½çê2ˆ™€£!(Šõ‡ŽM¯­ýY¹¯ÞVþÈ¯�oh�Þ®˜%aM ÈŽVo:^ÏY‹jï×DUŠp&Vfí„¿$hëºmžÐäû¹O'¥'sœê2ÁŽ²2^ [£ø¬ÁçyÓ2E½£i‰Ò_Oç'Ð�¯… BêÎ³Ñ€‹}HˆÀx“²ö¹ûî›(ƒ Ç¾ør[öûñmøqàN¿ón©Y/8!ÜIý&å�<§B§Šöa/Ê+K�ÀáÝVÌ^Çß}m›…üvÚò¸>g,§B;¾Rd© wOÂ#„{�[Í@€ÉdýEàÑo’ö±¬>…%?=Ë	#$LÂ4ŠýÀb‹ßŽ'hØïMs�
šm=àö°°=JÕvÜSoõôãâ>bÃ¿ûn…ÿŒ¯ËØÁÂk¼âg&€GÕËM¡´eFbµá‘a³DaºE|ÆHg’>»Ô!®·vŽùO7é[µ5I¦j‚z{|”ú�`æ	ùñscs Z²å�Æò°Ç´ÂEÃ* •’Òá^¦ØˆÔå�}9ëÿ§œ/êö}pÜ2j! >cH®uNxkp½2¨GClï.w½ÒBÁì„ðÙ³mÚÅ]÷l#ÿôßGÕßº°ØQ<³D;ÚÐõb*[²˜™.›3ÞVå¾MØNÂå·q_Ò7N¥Uã”¶·.æ“´<‰HJP!rláŠs&÷®égóx^É³·¦‹qÊ“?žE|ŠLŽH.²{‡«A‚¾ä”ßâ@4øèš_¶SQæ%µ›™õ[˜/â®Þš8è*©q4Õ	üâÅÉ©{¤ïM²#OS°Vc,ñzŽòöGð¼0¾ÆAä/ïoÃ¾‡ }pÙ{ÌºÞ]„òÜQ”­DÇ¸—‡)lVç�Ôa^çÃoÃ¾G†œHò^Â=Š™†ßOÄrRQ¬<&`ý†©I¾_Z9Ç¹‹‡¤ÚÀjº¿ß‚xï"Z1›�öä!#bŽæ _ýljè<®I¯;<Ã—ç®û+¸×I‚CFºY `b³�2óI2VƒT»x9€¼{'ô‘$?{½)žÞâ«ü`Wÿ­éô¥3AÆnH6ˆ»¼LÀÚWÓ¼„×âx!çârgè4 Î±:b}e9¿	új
îI5#¥@'ÏääÊßØ”êˆe¢¸5HIT3m^«?æ�¨äýåÀøõ…>å�±ÚÞ^']Õ<&+g÷ˆpb~ìMA­)Õõ7_Oýýdànö"²¬Kµê©Œ¨ä`×Î–�ö‰cÜûËcÿnÚiÊ›-_^›Í`SVóLå°&Ö¯œS§ÜÓˆ¾su%uZ0•ÕEâ»üú}¸åC©�Ærv5µëÕÃ#!=jÞùGÎ9·ã‰*â©bÑ£ôæÌÚŸm7aÜŒóø3ÞÂøãå��Dç«EÈ0N^Ô3R6È€BËêÀâ Îa½rbÃØ÷Øç¡¿z½ú£�(‘îHŸÉ—2¨Û!Á€<¦wÀ¡Í DŸR6).§6½u\=j{{k÷­äñö@è‹‘·±‘1¥nú†ËµdmDI•ö z§|¸O4Ì;ß#°ßÂ|-®føº/T¾’µKä=ã¶–ŒÃïç@ˆQ/—2�t¿‡~kzò Ì›‡sœëÇ¿>ßüü¸Éðó=}¾Ö�]øØâ*}vã?f”xWïÚ‰þ†®@#¬j—ØMÐëÊO]“öLeí¦wÑUø¿"ÃËp`V«º‚§sÐ¦�ãY<’Äé\–¹§¹à¸ŽB/k4¦|þEiâéyÁ÷IþöÍë øÙázYÕ
A«Â*žg½™ó‘B6h³ö&·P¢+òóû³¾Öuñæ0Ã#0E'Ûâ²™F�ÀùÚìØÈÊ<™…w@ÁÃ_ìö³°õQ½¬®q7SÛ@�1•yÆIÐS�ö€ÅÊ;²Šýž=mØ˜ýàëyœ¬�‘¡Þ9Krfµ²eg�Ã«:?F�Œtwsª^¿9eÔsá_ã¾ìþ!/RÜD m=E»	çD«›î~âco<Hx‹É@³w«òÑü‹Ñÿ÷El£²4�\L‹dZ0zÄêBwéšiD¦ËÂÙ·W}äz•þþ±Û„ñ«ÔºÅ¥Òyê]o|ËìôKÈæ*xV. Þ‘mO¤˜7J­»±pÿûz<‹žè+ úf®Ùò”²û^¬•tÜmYÃÕ—¶ñÂEïc=ñð¾ÀÚÇô–/[rBwÆ­SëmÍë�–au8 ­¹ø|G2A,ó:ŽÄ©ý¤OçžG«ý…–emp@G©ò(zç"Z)aãrŒË¾�ðð�óò�;
Ùv £¤Ré†‰ùc¯ŒOÜûNóª[P),ÈÍº î_â¼:ÒÜ–1¶÷d_-°qy Uˆ±î˜&»:åiÝ¹ñØØç›­íÏÍ¯«Ÿy¾}ý×³ÏsX"]ôkqiM]&¸8˜®[õ"ÑC‘1wu=q5tèo�^Ï5¯w'¬%¾Ð÷ó­+*Ó˜ñÔ–}ÉkŒ=y:äèjnÒÐóŒíßÎòþ¯>±Wß!¢ä‘WQ?B„ÂÐ‚#ÚM £BKCa€AÂ®Lá\IrÙÂ…Ž€/V):TßEã(AÀúK6‚áÇgÅPoµ~:�`ÿt.O½ôÇÝÍûD½œâÐ/¹ˆ§@‹£éª9„eÂIÉ†}üm@KK±åØÈ wzì…|†I1E¡]oÚ~O!³°¿ÅzmQnXdeˆ¥ÜÁÍoža†j'„€pzæmt¾u'Žùß»Ï÷]aä1Jü ý Æ°G~ÐO±‹œÏ'æÈ"qzƒÁ¹«T½—¡1Ð*ÍY «Üh¹Áê?ãxQç.ÀGa! ¾rÎc¨…Åž&é¾> …PÈ°¡µùBõð~úû^e£aÂßnÇ#ø��†â$ŽI�_Ï/ýÙd–­… ŒÅ�Ýµ4=Üš¡?Àœo…KZF®OäÉK¿'C?B¼»tL5ËjbtËPÅå*Î œ_}ZCÂÈ'ñð%æ—ÆWyzµ¹2=âHÎy1I—»¢¨ÙÆY{¦†9Ð\
 º•Á­ÓÜÉ.Ü/xM<}ZL„!�ñ†ñq€a€Ã	â¿.Ý‰>ŠSBuíŽÇ&2LÙ±ÙKœÛ[vsÑ)“[ôÒU_Çà+ñ˜á·'\Èh aÇ!…¿6î8gØ¦iÖŒL�¤ùÐK´ ((Î:¤”³®äˆ‹“ìŒàø*ú¡�Pot>AÃcIýïS=ô�aìá0ýAøÉcøh<Æ}ÿ“
ÿØo�1hjpÅX]è¥?ª&ró”)P…î‹ 3ê‡ŽD1<âÿè5,x,Ç�œUïn«³_mç[ Ø~/^}T‘|«/¡X˜Ð%®_ÝäÛ¡h†<íòc+ëb„w„Lr:³ôÇxþq:GÄ«5†Y„ø™Y%}Uò3Ö‹x.ÏZ²j–|#_cÏXì±×Omùpé™ÎÏçbÍÅ³N~]éãÖ„Ÿ�A!£`è#o…*	?ÝÁ2îj¯¸-ß÷9ÆÁB6|Ž‹®°+=ó}`IHnñÕ¿àB>êbèlvÙ:%2“9"Ñ…(\g·Rç„‘|Ø‚�4é_=ø+ÒëM—û ­‘’Ü±¢<ÚâÓ‡N!A#ã¥0TÓ4ù‚(ø:ëã6~…‡g…1„ÆèÀpù~H}zCÉ<SüÂo!Ix@™x6fSY\CÌf5Î:GE˜‹ÙW¯ò•
{3Œ>|ëš$Q„DQˆ¦_ÿRk…ÆôÍPÄ‹0:äÚ�4®i~Ò*®²çƒ"x¯ÂW{ùJõö2Ž=ù„BÉ$ÀKâÿMªÏfÙžË¬ƒUyÈÈÆŠ;ŽK¨™=Ñiy˜NÓRßŒD^2¿úÃ¸?Âç¶"¤aªÍÄ…«qŠÕ(�¨‰v@ÀH°€—.RmÈÏ›Øôš8Š
,à’•ÙtÒ-A÷ó$É&î·1±Å¼ãÝ�aûGäüu9ÿq'ìs”«õ*%XºcMQÊhÒ«Ð…„5k–ƒRÑ­Ç‰Ü¶yÕ¿®¿á}<¢*ëö�XlþðX¹�ßÃŒÐMö®˜Ó'±+ðÉÀÀ°Ê×èïéSÇø#î»'#�Øq3WñrÒn°C�œµAÌxµ98‹Šiþø³ð‡hA0tÔ„¢ô;DuË¨º†ÉÙWµ¤è›KN÷‚Ž¢¬¿AzõZ˜«ú—ú¶Š¯=øö¡ÄÜŽ›°ƒrQÝ“Ómlƒ	¦^›¯³:µmõÉ´¡ñˆPÈGè‰Ñ>“æcÈëÑŽÉ"~ärÙÉYçw'Þô1Ö ³[å]•¶Gœ.œ@hº{ÈŸö2ì#I‚6rh.fQ�A	\³ÕŠº‰pcï¶ACr¬>òòë*ùõ"Ü|,Õ±%0š½!¡w" £›¥óíŠf-’y-v°×¤ÝàE*¿:®û¿O ?…áo'æþØd1Â�"Š~uáˆïM�}ò)3–CZ=­e%=Ëú³S\zEFôsR»¿C‡<\á�8”¢Äb‚Â1ôŸÓ=’Ø'ÛòV÷hæçãvðÇÞ‰s…]W?A…ë2èM�;Lô‚=�ê»}éxÎµ.©ËÏGÚÿê5wŽÔ¯1õB£r<v§F)1Ð2p|ä<=�7‰Oœ›uÃž	ê×•ßåï–ý½ÞÈKiŸ<v,÷©îÎ'çrKÙ83H$»ƒBÀèž¥€pèWp¯‡AÙc�©ž¥}Þ–’;ÆvºEÂÆR–V˜‡Èul”îV…ß¡¦qó|%êõê±b‰!‚C�i£ñˆ ’€Ã÷÷ d¼ãéá@µôeš|ˆ«7zˆØµ¼ƒÉ™ÄÍÌ­‹­OR;þ—l¯õ"°$u‡–ƒÉž7`:\Åå&Dè°a+Â%)ƒ¾\vtÿŽ|y“åc0ÿ•0=K0ê‘ÃÐJÁ#ž‡üçMûìÜ«
;Vê
Û#Øß&�d—ÌëÇ+FL>Mœ¶Ø‚&TÌÿ.ùkOË…eØÒ˜jL¡©o�¼¡_e¥Úïd'ŒU““‹nâ©¿+ÃGÕô7 ¦ú&§¯—Ãšä¾AKÝtëÃŒTW×¥C¡Q˜»v>1ø¿Hþº)û2£¶0Y!¼z»_ç(ä ë×$äð6©›Þ-‹úV†�Wc¨�èò65âÔŽ§æbi‹dÕ1�ëŒŸª+zÈOµZ™‘�;ãñûñ|�{��Z0Ršûš£/3˜—fY"B!Rôs‡r…È`=`ß¡¾>Ööˆáß{ ›ðÖLkC~c•@’Rq9…vœ²ì,´U˜—�Õìo]ÐŸ0_ï-›	À§áÐ¿R¶ToÄ…ÖÂ�¥�”,Y è-Ùù.|n±]™¾Õ¦žàç¿¸yâí/uÂüV@ Ác„zLà§‹³-‘¶ÌìãKeßQÙl®•|á¼Çÿ^´	f;¸ûü£Kæ‚ÿ•(Âð#ï@±·ž
šB2$Â÷Ä.1†O"§—KØSï–ÖõÍ¤­¾ÎéÀL~¬úÞÈíòŸ‘¼($Ë³I‘ÈU;"êYHøKJšÖGÈ€övyájü)Nò&ÌÀj®ƒx(ü7jäã9æg Å@ú‘ÁGf–Í·N« {â|ùvx+Ô‡«Iãwz÷S2HMgTdŠràS8öŽ¤ Ö›pMÁjtÇ’3Ï¬ltÑ__áŸuÉýŸý£ç3¹Û5(îÌ.8½rœ—p’høt¥³L«kGo�º™®|%ß#Çoq^K®Ø¼•äN²œpîgB 8s“/d¹“ƒä-¹Uk€�
�õøÖP44áÓ¾Ãð£¿!ß£Ú�ÊâdàmD�²ùI†·³Ø®Í=Ìw|‚‹—�ƒô~ÙS€ëbo�ºVq²~Õ.-H)ëÒæ«,ÞMº€×Z'jú
øöæ×ýHU²B$ˆ@ËþÀÁ²™D2€Å\âh:#Iß"b‡Ù^ÿëõÖÅ¢–»ãó%D©R+ Òj¨uÂ½má�Ón†�´Â/â½]Ÿy».õòø@Ax»f&“}…p,Í
67¥8ž;‚;äÄÝ…[!<qUÿëµjÚG§ÏÙ­¿+%Õîú]økÚúâØ�X¹uŒÎ²>C¦áGœxïìj¶>²©#N‹ÙyÕ¸DéôëÍ8F…TÉz&ÿ_çµã,ƒáÊ½Z(C	%�3 ôÞÉÕÿÙýö—ØH›xdaóÚ–Ç3ÕVx³Éƒö?B&‡ùˆ<ú,M¨8?/J�f<Å­Bµ5`fGëÕcÈEñ5Ìxç}GðÏK™ÁÌsmÞµW;H¶
bý)Éô³‡ÔŠ”…z ,j©Øõ!@{UUˆd%Áæ(a;�’+¨Ôdì„~oƒÆ…b‡97uú5úÅëÂ¨€w·›œîB·V”¶ÅæN²ñV a�“Þ¡ ”±D°gŸ‹^MH{ÄsÛ*¹z¿8HŒ(ŽŒ±÷æ0gÆ%ªŒ…èL–NN´Ö‚7ÔEô�/©Ÿôë´®×áe} Ýâ›Í¼zœ|�é°`ÉˆÊ–WA1O½Zÿí3¼f%ád(À5L€-¤ˆÉðq‰WPÅ×²ÔÚù~RÞ|ðµcûàþˆÄžt½…:�9Ã)´L=âC¹=d2zg%ÉÄË p›á~dí‹—ž�WEvÒ¾¯a	Ÿ\�Ÿ¹ççÜ	œ‚t,tH�§™’½!“¦=7ÛáG‡Àz6ñÊ©ØŒ¹œšU4Ÿ™§¨OhQužy$Bíwu÷"�;¨Ää¢kP0ÑéÕò¶âÚqFG†v¥ÄV)P¾ù¢ëçs€Kq•.�sá–c�kqh.š�šÏW •ÑQlxiZ®Î„ÕÐavFÁX:0õ¤§÷Ê·ÓmLÝ<à.f…ÔþuÚîpÌ!(æ¬}Ý©çÿ�ð“QÛ£“R„h ¦ƒÚä[qçÄ‡©ª¶ÅMj5½å"+Ôú‘µ/Ç±³…•=9-îæeeX÷¯÷AÉ?Gšl0(Âòû|ã·LÒ?ë(uðÐº³ÐæäW„,šæ•å“2ëÓB±fÔáI¤èhÀ?¡öUè¥<¤SB„\ÑE2lÖ#*Ã½>`]õ�û»×«õ½x‹¶¯¿?�Û&Á±²ØEÏ=F¨1 ™ØžtÁfêÝXžBNjm°ð'Ô>œª"{é,\uÖVz7û0ETEj°I‘<^e�ö7qîª¤Ý]Št­)¶gnj(LD¥u†•X�yIîi[A< †X‹ýš¥éÿÚæJOcÔ=¤aE[8’Xmˆ'¹×Ï®î¾“�±�]þ•ûþÒc‰4½ƒxœÍXÝsÛ6¶þ
”“dÈœD&owNý HòGÇ±UIiz“Ëx YSK€’u÷o¿ÝøeÉiz÷r™V&‹ß.ö6åá=_¶áIÆÅt,X™%«DDìº|`â!Oe!ŠSÆ™J²u*Ø…dË$ãÅ˜¹fb³‘êí�O‡}–d‘xŠÉL0Y° °0	ŠmÎÖ‰ŽË¥ÊM�–«$0üEŸ%°pÄÊE1 -ì¢àyüó5Ù:¼£ käé3žE¨°T‚‚8<Ú$YPžêd#ØpzE\«BfZÀWJhÔ””½\,¦,M,‰Â‡ýñ¾LÒè”­áØøÄ²¶
C6+³SÆâ`Êl•¬Y tTäÀXÀßóMj‘g²ÔB�š·“€}ß?01sÉê‘ˆÞáë   p[ñ4]‚G=ÂÙVù¿©?Ã£“m¬Ò;ð†Üù“›_Øûêûþ#�%°ì«íºÏ‚T®%>C¿ÅRi¶,xc^7JÔ=“[QI$Œv1ú$þÓÓ*Ql“P0ÃN;·o«ˆ	^cg$V¼Lµ‰¼Æ;OQ¾ª´\?kñBíÜo@¤bÍÃ=ãiÂ[A>Š®à(Ž+¬¯ª\áÑÐO»
õé±bªòä@3“&ø>[ì/]Ïb1	=š} ÔÉsøï“XÎex/4«™ 1ŸE”æº�õ>Zêå­rÔë%›\š¹½'Ä|}Ð<Š,”D{ð›’V)_Óß­CxâŸLè Ö:Çg©Ìo ’uÆS|É¹ŽƒU’
|@ @‰QíU™„�¨›Óóz½-/Ä¯J Tœ1'[§×[•YHººûÚ;APöÅ£¨€ó�1ÔÎŸ¸ë�B}æàÿ­be**„ÂD±A{ú÷7ÿxÃö¹î†ãñÌs<€Ž¸æã‘ŸBãJ=†g%…µ·Ö¨/ÆÃÅðn|5Ã²üGà×ÂÍF€©S0ÊSf¥AZ²VUcî‹É¯ÓëÛÙdv7º½9¿º0�"ãËT|ßÌ+È÷R¦®³‰2PP¦Ì{½Yaª™È–Z „„XAÚ�dƒŒ(BÅr÷‹h›Å@Z�U`€5ÎÚ‘¦a$1à?Ú8å….xü$Y±×0x÷âËŸ‚-ôÊuêbðœöRyÿB„�ÁD‘#¯ë¡~Rù�ã¾�—G]GÉÄ“CZ44‹ëÔÞG“SLÐqa{ÎNÏÀ^MœÓÛÙÂñÞý‚{õê‰³›Ätä8§ûË�üˆº‘jU”5šÅrÖ»³B?BU¾
ð¸¨O³Á¨]…ÚÝ¦˜ÿ“„410Nxx,
\Ï«…·#°±L'.IÈ“X£ÀüK(à¥hD·:€u&r¸Z3³z øCBøZåZò¬aµÎBØ”%©±?”(ÿ$¦IŸ+[|±ø”½Ü:„ë§œ *g,¬Î¯þøÀCjíÂö¦'×%7cÌê‡>yŠ�hk°ÿ	Æ¯QÝŠôêö¦�,¢x‡zƒ‰jx0• îŽbÄØð{áBgFsªÆ}öÖ#xöo¤NV{—øûÌb~uqu³è¼/&³°¦2,ÂXÙ�ÚöŽ¡y)QÓ¬c_—f›ó¤RŽ=’‚uoÄn„5ffiâªÌqQ²hsÌkªÛ2¿×ÀùC­y·ø ‹ãÀØAa`‚Î‡8#SáÈAþ\–Ì?à2[€›�ÂMµ (rC8×±P«ŠVÛ÷IŽµáM7î@"Œíø³ËÖÝnÙ]¯°¿«–›Ü¨UÃÔ89Ø`¸ZU°Õpø³Þ7×¼Ð¸2¶¥Ÿ<�Û\x€	Ü…`5ñÐXØÿÄ¡æÒ˜ÚŽüuÆ‚Ï-½×¯!½Þ‘œ}&e+ÔNÒ¢Š¸<�L($ÍA%ñ¡| `ƒ5ÿzO*Î1v�‹É‚Ùy+&ÒvfçL¨\fJ|*M$“¢@ÿ½JÓÔq²ó/�”b.4ø„TÒƒÅ>Xyž§IÈ5´&33¡_ñ›à4{wžoÝÏ?›aèÏöDNg¥ràšãÈ{§O´ª·žÖ}�ÈÆÃÈZ»h…!®?¢yžµÁss«Ó8Çnº†ïez;ÿK0�5ÇP>~ÄŽÐ‡Xs¨¹âÜxr=YLþÄ™Ø@Ø³ÖÑñº€YQ�˜Õ#ŠØ¼;¨ÿÄbç<sTŠúEP+Ï?`€™Û´‡ôšl7K†Ø{®B4s8Þ¡á{çü5î9{1UÏ^na‘‚¿ôÚý³ßnÌýÃèo×ºƒŽƒ‚
Ó+2+UŒâ+"Ÿ>‘c|ätâ¹KðÈžÎøØéˆ�Naðýk:è0‹ßíví©0)
£Å(•JDßœ]üÁì‚ÝõäÇÔ`,3A­ºð'z8:], Á›GÇÞ¼Æ ÁzT�I1Ç�Û–ïZ©^+žNZæ¹ˆ "¡~AÇ¸xA3Ç–\‰?D©j)¦€Wt™ÝÎæ,/Ä*MÖ±f±q”o.Šoep.ceëM¯ó†¶.„.‹¬C¦¼ýŸ“ò×A;-·9¦#¦»“I•%«eåá®ó‚o:ìãÉÍ?�òÎÐ#`ÕÁTBºï‘[J¨²HÖI6ØÅ"„…TÊRŽÂÃnÅ¤m!ÓÁ0Månpkøòõ÷oú t,#Rª^Ÿa£€ß�ðcÊsŸÝNW·ö>ù]˜†…0Ûöì3¸¯ÆpªS™«.lP¥Hœ¾ÉEæÕZÓäÖÎ'?ZÙ¦Pç½‘V‚ÙÀ°—$S,°@¸;ˆS!í÷	œÓîÅ¾_d¦»{ö/Šý¶ÝÉv@mØvæ9
Ç-])ìKÙF7oØná[r—°âÑ/*È‡0¶\Ê_Èk¹ƒSwU  !ç˜£‹’ZÈ[üÙ1¥p­â£›>ò¼1Ýæ£µç 9ß\	!‹åR˜v¼ó•#É¨@‹Ì"^DpÛ0ýLÙ4o]+«kñ¡ÁíàŸ7ƒÿç/f•¾4t.ÍQÕRœ–�S
žýæŒË�UO¸«§`¼†Aˆ¹¹íg­»ƒµQómÀÌõó¯˜O²¡xÅYínÛÈýM=Å„€w©D¦œ`·X¨ð�$n±)Çµ“…a$9²¸¦Hf†’­ÍØ‡Ø'ì“ôœ;CŠò×nŠÍ‹ÎÜ¹çž{gR'éer¡Õ"ÉËÁ _Ô•iT4B]¦U–—ãŸlU†˜-þÕJÝŒçMSóÙ6-›|¡Ã.òf¾œÆiµËëY>^d%fƒñXe‰^Tå©6«<åÖµU‹ƒ£SeýH³®µUM¥š¹Î�Ÿ®òrVÅƒUÒøõVí«³sè°Lõe´b�V
ÛÝ‘=Ž’…Vø·™ätR%?Ì*£`§¯LRf[ó¦2R=Ë¯eb:‡ë”-–˜ƒý¿„aqlÒ:©�/þ7¼É÷Ÿ«jó�/üŽßö{ñyóÏ²:ÿ¼Ì³vÂ<)î‰p¯œ&Oí¬:1v#HÞ8‡œr#±8mÓ¼…÷r›V+mÖjjª+‹ ÐIQ¨Ë²º*Û(¸ØÐ+É²©v�¾Èm£�•¸z:Sâ«WE•^Zµ,›¼`8•[`ÖßZ€¨´*}Ý¨Üª4)S]:‹³e™ªÈ¨§¯éÜÙÃ¬‡ê®²Ñ�1P`s$<%ÒâÎ€‘v|)»JÕd_!~Àû-eþ; š4©›¥ÑÏ ˜Eî‘¾Š0#ö(ƒ;õu]TF¸vá™÷Oàÿ•øñ4«¤ˆžï=ez`)ŒÎ†ÛsaY¡ßãsµl¢?Ý�‰ÄqšÄïÊc­MDE5ž”3˜ƒ#õS•—ðþ´ª
qLä3õÄ�ÒÈ 0v•|¼áà<>Fþ6³(<£êçŠ	´cUÒàïd‡(¤áÌœ‘âÆñË,Ã–òxÚRœ‰kSMõQ•iÑîö$'DÒŠóoÄ.è¨�¡�¹u,QŽ†–Á'ûªtD÷{t…jj–ä@ùÂÌµ³òÊvQ‚xQ�pËË%ÃJë¹ù#©k]f‘¼ŽD	ÌF~ÜçÉð�¤ÇN¶•Ø¶Ð¥“3¤i ’d 'k¸TíXÀ¸QfY‚âæÀ=Ø#Q•Aä¼A`u¡Ie7>C;‡ªÏKmr$f¢JøDJTÌÅ)ÓÌ1©�‚ÆŽ˜’H¹Òç’s×ýðvYÚ#5È2ûÊªÜ�y#íÃ™Ù)Í+Ûx^8XN Ì¤B=ß
4§‰Õ9êJ|Z{²¤LÆãp”å„xgþX‹õbû  F-zL�¾†w#Š~ŽÁ+c~GTB)º!0ß` BŒ=òz »cbSÁÍã‹›ÈÎV›TÄcóæ`¤>Ò:î{º¡ð<Ž¼îEvàÄ.é ¥xÊz‘ÜÓ{â¼×%,iÕ¿ý¡fv[õîèð_ž„ÊD2Ð–¤M¾ÒÅZ5¥8Ž¥ïJ­NŽ_:¹Ÿ¢Ñüª�åH.€”…L­žÇ/¾‹_üðËz¾ÆûNÑG¼ÄþœBµ‰ªŠ‰BÅ­ºŸ"Aî3Ù…G'éÜŒpÜDÅ-•U£’È#™å3¯¹9É¤.’i»WhC¼¿Åq‡ú"I×8¬Ûh'¢FŽTuID8¯�…î—˜8;GZ:0>ÁRÝ,KT�,;o"ä¶®J»¥¡çxÂÑW¿^íëT’­Ò·IÝéGèÆÎH½VRA¼ZÁ-þd·ä�N1 :¶`äÙÖô0ý,V‹.Uœô§ÀHÒ<Sûû*]¸¥¾KÛÖ›áÞ±ìlòÃ9DÐi�pæÙø}uX]¡”¶ï'!OõK0ˆ3,Tˆz¸‚ÊEÇa 6u^ºïéíg«gB í®âí÷èh=h#qãHä¬çf²ƒ=Ð³dY4œùÄÄ?&í@$eR*n$«_Wå,¿�¢x
«&ÒËâAšŠ#È“±zÈlØ&Û+9MÁ·cp§‡ønsÝ	u”u�"ÿ´Zš”Bö¡ŒyÍ‘c­ÎFyËmç,ƒø¢;äþ¬èhŠËê )wŠ¡Ë<µd£ÚfjŒ¤�r+úZ¢ÖfW^„ÜS0!zÔâª$ºœ¬*¿EåZÖ¾há´Ñ%Ù(|w© _íœF�q%ÂpL¸=`>}EÉ"Û¡Ÿ&Ã±iXw.RDÊNñ"_‘ì–<£tÄÖ£�MGa;v`3nÏÂÞ§è«_=ÿL{Ô 	=ísÐ4ýïIH=ÊB~Æ#4ägÜæ!! &”'¢_~yŒ” {$Ô‡“C¦òV‡óXÒa¸ì‰À)9¾‘ß"=Ìüÿ°Ùÿ€ƒœUÂÁ 2?À‹¿Ï8·˜ò+y§�
îjü-ºM;ÒÍ5Ï³Sâø	]8ÇÛ®›Y´XKôëMs¡ªrÓ…Ådœß§áÚ}ÜŒ}úF†«èXg†RztîìrA]øÈÁf¯™ƒ¶:‘“„¦XÇuÐAº;�ãn'´7lg<r—è!rè„[h¦ –GS6X9£[íNâ· Ùñ�™Là´Èu)Uë6ðñkyÿâ�³õB=ùþè‹“×´ÊÖL�O_BÞ/±øLÂñ^8
‘š“ç£p¡Ñðá1ÄïG1ëŽ ¼¼H6œœ�ß| í¶è]üÙÑiãÌà3lŠ“]‘§	­u7Y(.îº*ÆQþD'h1#jÃçPVï4àˆŒ­¡ÅÉŠsÇøæÃÈÊjfžñ^âXõãû÷Ç]/¶‰Ê��Hä$®ßíí�¿ßÛÛÌY’ô5ºF\¡¼+¥^Ðx
ÁÍ„_ÖZr¯/QåO�Û™¥2�ÛˆÎ™¸o‘‹«†�ÉÌT�à?Þª7r¢t_…ðŒfEL¦85 §ìÈÄG•š'&ÃE!>ºuöà+Ñ½H¬Íg<¹^±­\. BQŽQÉ}‘ÝÖ¸×#u¸s$ò
6pRÔ¶§#}£þ£ƒÃ_Z�@Y"pH `ÑÉwKÐK5,l‘ëUàp8!�K¦¢.‹�¸ä||q²Z„C…êÐjtGüµëH¶¥g	¯U3×ÿõ·Õ�·i6-! æNz-ƒðhïb ¿/s‰˜$—–òáf#wimF›x±ŒOq©@„Co,dìCÉ«ŽúÂ.}­»T3±(à®Ó˜L1ŒGŸûîPŠ}ëPÁ¨\ÃHZÑŸghÖ‘SàæaÊ%€“¨¿�¾;Ú
æ(/:Èµ¢°"N/ÞR¿<Zšb„T!™ø�àBAH„—!CuëÀ#lRwCáIjûHÄ¦{CZÊ±ÇH\ìÃ•zÎÔ1˜òÛc–Â-~ûÍ�€ÎZn;Ç…”›Âƒ@nâ5ºbÏs$Ìø-.qç¸Têz„¹Ð3ÊB'ê÷ˆúû;Dý ÃŠ_ÿ½ºLˆ¼ÃG™¶¯o‹»;”ë.|}!„tóœ¸Ò¸/õ‰›„nzˆªüE
©zÚ[¼ÕÖò?Bœ²í¢…•U7í âP¹pöÃ‚šr€ë]zN*ÅðçPô�SáîÅ¦§˜-Û�7=vÚöÐÒŸÎÞR&àú�w=n¨p,ÛZ{û�·›tóœˆÛä|�¢ñþ¡Kxl“sx óÿ’4ñ3°•³¶\%°¿xÍX]sÓF}¶~ÅV3eä`dhfê6`˜&ÃC&²´±ÕÈ+#­pRà¿÷œ»+YvH™>4I¼wï=÷Ü³w½NÒ‹d¡Õ*ÉMä«uYYƒ0-�Õ—6Ä¿Ú¤e–›Åø¯º4(ÊÿmÇKk×ü¿¾2)ÿÚ|¥Ã ÿ,r»læqZ®Æ‹²Ê‹"oô¼.Ó£Ã �Õ±N
nxÑÌá€�µZ5z�.á�z§ç3Ù êf^§U¾¶yiê8°Wk½³»¶U“Zõ9¬%?t(>~wÔ Š`�¹6Ö“õéÁ¦žÊÇ3·ëó×àkàL¶3jk8<èœ�§	ôã4ˆÅ@çl^–…BTá¼€Óu8R¡­S'©xÍÏnf’JxÐ6�;—ÅãÎß²Ö™¢I:×ÃêH×5–×Ê.µÚä•VçeµJ,ÿ¨ÊCªô'¼U»yÞ	‘t€Iê“<	‰pø!L%\à¢lç%‚Q¹Ê­^­í—>OlâL%æŠÿxSÆwWž çµMVk•ûäq·’\�q˜sQÏ\ÞçúXl0Ç°7KÄÚ&´Ö&S¶l	2×>âk·!wS{!1£àØq3�.W#ÕÏ!>•Ú¼—±s]Õß–øk¥P7)”cv]4‹ß•@§U¦ÏÐÞÇüJoú…‘"¥…‘(£7Š5œ7&U»Ë¢¡:èïB%TÚ6•Q÷zÃmKaê^èèæ‚Ž‚�T‰× •T¨n©ñï.©¢,×±š&&ÕË^J,ÆœÑrÇ£!E\æ•…uD…²jm]¨ÔäP‘1b;‘‘è×‡êÀ�Í46fÃ` ´°Ôíˆg80‚šH~†WëB;%¤I­Õpfü¼4:N0?XÆi�âzZØ‡Ï&ã–{ÃS¿¸‹WVõ¸|J*Õ¬U’Á•Cµ‡·~®L—:½x]å‹ÜLSUê€z{2¥¾ÅkŸ+¨˜&[¦¯�gj™˜¬€ êUs©
Tt�ã‘š˜©—H©lìD=Úƒ	‹�öìYs¬fùßz>zøËcL¼«P´;3nÂUm(7§Ì­Ì¼89yãªDgÆŸ�õåº(+]�+¿ú¶ÌïÚŒÀb‡B½†škñ§©kè C¤Ü@&+¡FxìAŽ6Ø4R&/�Èü\–ýtÈÏ=L™°VÿÉ¯{­Æ“,4?!!pIï•EOÖ]=àÎª‰_BÝIIn•S÷÷Dp*óù+î·ø­áe E^gT+Š"³P;jyë^L1‹Zè½VÓMŠ4ˆÞËnwH—;%�øÂ)7Ñ0~kòË#\¹y$Ž³<XÈ „íÄgr+šãÆtê7^ã2£÷Èjö¦Y­#ž·2-tbšõªÎê¨ƒdäZ`fy<e¤F´ìšVlÏÆÍë¯kŠ…1ñL[†ó2Ç=~øÛ´;õÓEŽªw‚"¸<Í²èÉ^¥wv¾)ÍÂ±µŠ¤d	†ä–˜?¦Ud¬ì‹Öû‘ZÕ‹ŽèþFèY âs�â[ñß~ ì¥ Uú£ê.G¯Q˜òÇ¢vØ •«¤‚ú‘¸|»†¸ÊP¢½Úcšmnp[êoPor›.yDÜ!U*ªû�–iïÂE–/ôkyÇN¢«sMÜ	C9c0à†½õê¾
'!~w;h�8È^™ó#‹ã6XâÔènQÇk7r[ï–pWj™ÔðÀ5Y®À;gXÝé©|å÷:MGÊÏ!z	ˆANv‘à�Ÿ»h&ÝÝ²òÀù-Ú¾ðAÜù8¹í.žUe’�Vñ@t9Æu®l&’}…oÜ·˜q†�È"G‡ËvŠÜ—¶ê¶û§;$’®˜r
�’ÎÖA9RlRqø•ˆˆMz¡þäyr³oM¿)�¹D‰]f€ý°Ï�´ÀºF¼®äÅ¥³N¤ðŽ¶eÍº.m#C�û£7Qúï�&‰šxMœ—
šéOË5n#qÊ+ûq{Í¹vKn¾ãÝÙo¹äH%&o’Nþ]eKêvJ	¯ˆ  u;]]u@ž©/_nž¹�Ju)>#Y;Ã=ˆ3Ï³]CR'›È
ÜXáB¾›/ž^»Ç îÄ„°Ût÷;9r�þW0K½Â]„ø�ð¶­/£ÛigzyîÁzç4ËµÜÞëyK†Ão>5Ú—žMðÂp®Û	¯}¾=l^£+ÇcöæœÍ¸ žBâ]Ùø=&SZÈÉ‡�<"þ5©eÿýC4èxß8jrˆ\»�©€¦{ší:LÞ¶í¿æ€¬+1Ú!Én_µ¸wªû.‹ø½¬§ý¦©%Æ¾œùvªwÓ9Èå×1€e…v„ýZ&U½U™^¿!-üm×þÛ„—W/*�kèÄÂŸ³×¯ØU\ï':ºûßAÅoð½‹=�ÂS´‹¢ÔgjÃÇ�ëÖ&êçOüŽ¥ªvkâ:ÑÿÓ³ù•ÕÿC`Ú.pûÎ<ÁËÙ�ºËë˜‘!ÿ £Ë[[¾Áxáëpackage main

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/gorilla/websocket"
)

func TestRealtimeHubStats(t *testing.T) {
	hub := NewRealtimeHub()
	stats := hub.Stats()
	if stats["connections"] != 0 {
		t.Fatalf("expected 0 connections, got %d", stats["connections"])
	}
}

func TestRealtimeWebSocket(t *testing.T) {
	hub := NewRealtimeHub()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go hub.Run(ctx)

	mux := http.NewServeMux()
	mux.HandleFunc("/v1/explorer/realtime", hub.HandleRealtime)
	ts := httptest.NewServer(mux)
	defer ts.Close()

	// Connect WebSocket
	wsURL := "ws" + strings.TrimPrefix(ts.URL, "http") + "/v1/explorer/realtime"
	conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
	if err != nil {
		t.Fatalf("dial: %v", err)
	}
	defer conn.Close()

	// Read connected message
	var msg RealtimeMessage
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	err = conn.ReadJSON(&msg)
	if err != nil {
		t.Fatalf("read connected: %v", err)
	}
	if msg.Type != "connected" {
		t.Fatalf("expected connected, got %s", msg.Type)
	}

	// Subscribe to blocks
	sub := SubscribeRequest{Subscribe: "blocks", Chain: "cchain"}
	conn.WriteJSON(sub)

	// Read subscribed confirmation
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	err = conn.ReadJSON(&msg)
	if err != nil {
		t.Fatalf("read subscribed: %v", err)
	}
	if msg.Type != "subscribed" {
		t.Fatalf("expected subscribed, got %s", msg.Type)
	}

	// Broadcast a block
	hub.Broadcast("blocks", "cchain", map[string]any{"number": 1})

	// Read the broadcast
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	err = conn.ReadJSON(&msg)
	if err != nil {
		t.Fatalf("read broadcast: %v", err)
	}
	if msg.Type != "blocks" {
		t.Fatalf("expected blocks event, got %s", msg.Type)
	}
	if msg.Chain != "cchain" {
		t.Fatalf("expected chain=cchain, got %s", msg.Chain)
	}

	// Verify stats
	stats := hub.Stats()
	if stats["connections"] != 1 {
		t.Fatalf("expected 1 connection, got %d", stats["connections"])
	}
	if stats["subscriptions"] != 1 {
		t.Fatalf("expected 1 subscription, got %d", stats["subscriptions"])
	}
}

func TestRealtimePing(t *testing.T) {
	hub := NewRealtimeHub()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go hub.Run(ctx)

	mux := http.NewServeMux()
	mux.HandleFunc("/v1/explorer/realtime", hub.HandleRealtime)
	ts := httptest.NewServer(mux)
	defer ts.Close()

	wsURL := "ws" + strings.TrimPrefix(ts.URL, "http") + "/v1/explorer/realtime"
	conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
	if err != nil {
		t.Fatalf("dial: %v", err)
	}
	defer conn.Close()

	// Consume connected
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	conn.ReadJSON(&RealtimeMessage{})

	// Send ping
	conn.WriteJSON(SubscribeRequest{Subscribe: "ping"})

	var msg RealtimeMessage
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	err = conn.ReadJSON(&msg)
	if err != nil {
		t.Fatalf("read pong: %v", err)
	}
	if msg.Type != "pong" {
		t.Fatalf("expected pong, got %s", msg.Type)
	}
}

func TestRealtimeUnscopedSubscription(t *testing.T) {
	hub := NewRealtimeHub()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go hub.Run(ctx)

	mux := http.NewServeMux()
	mux.HandleFunc("/v1/explorer/realtime", hub.HandleRealtime)
	ts := httptest.NewServer(mux)
	defer ts.Close()

	wsURL := "ws" + strings.TrimPrefix(ts.URL, "http") + "/v1/explorer/realtime"
	conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
	if err != nil {
		t.Fatalf("dial: %v", err)
	}
	defer conn.Close()

	// Consume connected
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	conn.ReadJSON(&RealtimeMessage{})

	// Subscribe to transactions without chain scope (gets all chains)
	conn.WriteJSON(SubscribeRequest{Subscribe: "transactions"})

	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	conn.ReadJSON(&RealtimeMessage{}) // subscribed

	// Broadcast from any chain should match
	hub.Broadcast("transactions", "zoo", map[string]any{"hash": "0xabc"})

	var msg RealtimeMessage
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	err = conn.ReadJSON(&msg)
	if err != nil {
		t.Fatalf("read broadcast: %v", err)
	}
	if msg.Type != "transactions" {
		t.Fatalf("expected transactions, got %s", msg.Type)
	}
	if msg.Chain != "zoo" {
		t.Fatalf("expected chain=zoo, got %s", msg.Chain)
	}
}

func TestRealtimeStatsEndpoint(t *testing.T) {
	hub := NewRealtimeHub()

	mux := http.NewServeMux()
	mux.HandleFunc("GET /v1/explorer/realtime/stats", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(hub.Stats())
	})

	ts := httptest.NewServer(mux)
	defer ts.Close()

	resp, err := ts.Client().Get(ts.URL + "/v1/explorer/realtime/stats")
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		t.Fatalf("expected 200, got %d", resp.StatusCode)
	}

	var stats map[string]int
	json.NewDecoder(resp.Body).Decode(&stats)
	if stats["connections"] != 0 {
		t.Fatalf("expected 0 connections, got %d", stats["connections"])
	}
}
£Þ}xºÑxÕYmoÛFþ,ýŠ	qÉQ9ŠñµE€ªð‰ã\zH|��\q0|ñZ\I¬ù–]Ò¶ø¿÷™Ù¥DÉfì½Í‡ˆ¢vwÞžyff]©é¹škÊUZ‡i^•¦¦p8t1-“´˜?ûÕ–E€³¼æ�¬œóG¡ëg‹º®ø¹´ü¿Ñs}%ßmm°Q^Úe1†øu^Vçó8-ž-UžÅßÃÑpøìí- x¯,féœ=KmI‘ÅþLÓ”¤º¤´HôU<¬—•ÞØIÍ´¦/ÃÁQÖÌ	ÿœl:e­'�ÅÛ€X¤>T®o®,ð¶])ÏX)ºýü
Òëç?P{¦(õ)MÚÕ«ïØqø~��ÞÒÂTÓv1?bÝ6äÆ:6¯](Ï§é‹<¢DÍ#ªDTDWò9¼Ò3Õd5�•e†Ã¼Íð"¿mj¿BêQÙ˜)änùHÞ¶ËÇ��JD"ÒÅEDyRØˆT’"×ë¨Ù×)b”ZªAªÆ™¾Ðý÷Å»·,�iŒ¦Yi\m,¡ïÑ°Žá‡ÛÒñ‰<xPœºè‰É¾ë(°_ÔfI‹2K3²‚œâTe�¥´¶dš¢Np[«Zwä6w„;vD{QŒ¡÷ª®µ)ÈhöÝçò[X^RV^j3UV“Êª…*š\cý�Ëj¡‹xx¡Œ¬nÏØÅ)œ)ñ»ÆÖ{e^Á‹áéÿŽÕø·�ñ�'þs|òe'zþýõ_N;yr¨ç)X²ÓÜn´JÆVÍtÔÚ9Î›Z�­RÇÖ¥Ù°zuÂÚð¼a$gj|øË»¦ÖWÃ�s7X¡:f‹‹ùÉSñŒóPÉöÓø ôÍòôP«Œýý¦9óî;Ð—²s%y
­kÎò‚t^ÕKv‡XgM1¥íáˆœìÕ	Hw£®‚žl�÷^ñ	4?×áíê�"¬ƒÂÖÒ::‡üÛµ×üE’xÝ´Y,¦CmIh§3ç
D$-.T–&„·*ãÐ,I_Á2K3SæØ"�/´W&-M
Ã­dž7;4[fŽ
„ÓÙ¼Ëx#/¦Bò£6ãwªž.Ž$X¼+>B„FL�­³@àñ>+=ƒVW	ããÏAD«-ì >œ_0ŸíîRô�>ƒ�>7©ÑÉ:Õ©ÿDa¾õ‘,Ä½¢ D‡:‚ü˜8oâ·åô< 0mH^},2÷Rçš•ç4AZÅµÇ­)'?ñ/ì 6upDÊP‰ÿLš ƒ9òUež4°Î›í‰’ÕdîèÉJH‹/Å~Í£Õ–s›«E)xd
GQàÆ„.RE�mÇû`\†Ì9ð‡…y‘eT‚rÁ*‚;A~Õ(ƒISeéÉEa>úX%üŽÊQÉgK>cÓÀ(ïÝÑ¸£™¸Øèq’Ú)ûx9¡Æé�)[�­Ö*{ÊÈ@ƒmKl-ìð»Ïñ"ÍZTlÇ—v=-}q‘™ðð•ø=¨«Ü�[Ž9AKàxKác;òŽÜÝŠ„O ŸâkÛÑÊ•¢C�ÃX4/™ZógŸ!¿‘�ýÙíö…’�ŽcG®’H÷K€O7�Ï‡õ�<ì½®3•Y-ií”ÛL‰DEÕã+#¦­½…°³„µPåµgK�6�ZêéÝCÙyä¯ÖçY¿KÜ]—ˆó{ðN’êt»—�n9ªÃ�«´-ÊÌÖÉÚzæ(ŸIÒMr9Š�-EÈo»‚h~Ü&Öž][i°&ã=n�ÐŽbßŽ°Ú†°öW'ÏëŠší‘é)¸ecÙ¶qzÛjºÓý·á`cMËšH«Èm®+|Wœó.»þ©kÐûU	¦;S›º Q(þpüI–IeÞRh;•Fl¼?†:’¾\G´Î=ŸŒ]—EÔI ·àýµ© ù©´ÃýÙÂÐm¶Ë-yÜeUÙÔ\3¥MÚ8!¢�ˆÀÑ+’!Ý¹‰Ûh)³ªÀ¼ØRˆ ‘Û%UUºHB|ÁJŸØ {Óñ‹�êòim,Ïè›Ï¸4Ïàn5—Ç»< ÇÀ yÖãálUÙ0Ð+õ¶TÉktj2ÖdøÂr‰ïßd²™¡€}%&�3ÂJÕ?luú6eÃÎ,mŒö3a‘²þ±ð�ÈTd�¸·¥ñ;q{‰ÎaB�/AK,HŽw<rÌkÊŒµ:eûXäÊØ…ÊB§Í“Ùè§{H­°I÷‹õ(™®Q2‹%¬˜1m{&°¥ë�Ðî0tp“ÉžØ¿ª $o�p6Fc …Q­S¤Ë“¶ø¦¾}dcÏÓ
:»&ÜÉ…Û.¸…‘¹uµ¿m`< ‘�[Pºã¤c`,µ†Ž\0 ²ÚšÉZxíiiïÍ‹ŸÑ•ÂFBÈ&$÷ôÔ—Yyˆã8psÑ-BçÈ³Ç
kÈ‹J¥‚3#IëÚ¡O¬yGDA¸!AVîú#lüÁ¤ùQ¥¦ËÔø¸5«b7€7Ñª¢pÍ)¬Â°ƒmÈ9� i—çObŸÉòý;ÁÇWØ,+—aìDyriÜVÕŒ•:ê‡_rSÄ£_«d×Rè¼s"¢ÜÝgà+“dßy÷çq�å…2çðêïcv  à7€ßM¤ÏæÄ:Nm�îÇÁM¨»;�éÍ‡ïér/ãIoä’Ç½è)K¤,ýëèßýT¸Þ^_‚élUVÿ‚éWÜYèÏôÔÿò¹ÁÕŠ ñ2~rÓ&ÅG¨ãB¨Çì?D;@™‘)'-w7	Hñm_ŒQ~Ÿ/-±õr»Çî€*–€À‘z€±)v%”]FBIÂK¼õe¯¯G¾68cøF`u}�aòÒÇì´Éò.—0}“G˜ÎÌ$w½5þ•vÆ‹Gß©«—KÌ#\]Ø!âíø%Ô‹è‡�Ÿ�F±[>ÁÑ7&‡ìómï=ý &•9Ü&¢\_ŸF.´G¸^kìK•â2€#	—:RåÆò–öÐ—º5«ß¦ÉCð0¸ŒdIbŒS‘‰ ð[Ç}5.­Wös•t·(áèèZ9„Ôo‡n¿¦¨ñµN¾ŠnÝBïœÂî§Á¾	Ÿ2Ârõç·œ‹ÿ¨¬Ñ¡»|‡úˆ²¬Xßúô‚KÖµ÷IÁ=Ñåú�?u‚tÓÙÄ�1Xæß›Ùù õçÄAY¿æ—áü‡ç„Ÿõ"ú$m!÷t2›AØC¬è×ÏÚÌ‡‡^Î8¾÷W4®d@º=2¨µ|+å<-ÝÕCœý„L°¾VùCh´�?´\Îj,Š=þhÉnâ«GW¡*)û¿º]jâêJ’4xªUûš:£:�J‘T1“;legacy `chains: [...]` YAML shape used by tests and the
// minimal config. The full Config struct in config.go is the canonical schema“p²“¨Œ“E<	sup    *ChainSupervisor // nil = registry-only mode (tests)“ZÂttachSupervisor wires a ChainSupervisor so admin/config/mdns adds also
// spawn per-chain indexer + graph goroutines, and removes cancel them“¡AttachSupervisor(s *ChainSupervisor) { r.sup = s }

// Add registers a chain. mDNS-sourced chains never override higher-prioritLy
// (config/env/admin) entries; admin/config/env duplicates return an error“¡³œ“¸.“B.mu.Unlock()“Kd“ï.mu.Unlock()“K(“0“{!.mu.Unlock()
		return nil
	}“©1r.mu.Unlock()““ñ?)
	if r.sup != nil {
		r.sup.start(cfg)
	}“/	t“¹	%.mu.Unlock()“Ú	,r.mu.Unlock()““
)
	if r.sup != nil {
		r.sup.stop(slug)
	}“5
““").mu.Unlock()“á“	“"³8$(	updated := entry.Config
	r.mu.Unlock()
““u@
	if r.sup != nil {
		r.sup.stop(slug)
		r.sup.start(updated)
	}“�““!Õ³÷>d using the legacy `chains: [...]`
// shape. Full Config (with brand, networks, etc.) loads via LoadC“n
“O„“Ôv“K\c.WS = os.Expand(c.WS³˜i³/a³I2“�&“¶N“ð[“‘5³�B“ÄÂ“‡Ø³`p“ÑÁ“‘Z“í-¿—‘ô½±xÅZûOÛÈþ9ù+¦–öÊì¦eY¤Fâ‡òèc/¥ÂÞGUQOÀ·Ž�µ·â¿ß93¶Ç±'$öJ­pÆ3gÎœÇwã©?úæ_I1ñÃ¸Û'Ó$Í…Ûí8—w¹Ì<Èx”a|ÕÿO–Ä4Ë¼�çSó™° §Á„×Mýüº?#I4L¯AÇént»ãY<CœÊ«0ËÓ»7Að&Žðìæâg=ÕnˆïÝN*»âXÎ÷¯Ád±À•ŽLù]êa¹Ë¯÷“x^aQç,š]„ÎhDëœÆŽý‰ä±ý—<›OOöiL8t¨A¿%#?ºN²|ðzç·Í¾¼Íû—£þ~?�ŽxþðnªˆÈ›	œ%³t„!gÄ›ÓØýF·Žñ÷bWÄaDçèäÞ[?÷#Ãx�`Nêí'³8w7hæ+sÞØuäíTŽràŸ¢'®’\ü8½j�&Åï3Uê±jøóæ�B{±m•Ñ<%4½]†íêtxO ¡Çñ�…íõ$ŠTZtöÞˆ³£ów`„ÕUèêÖ1$½Û”t%@È<IÅÿCÅ¢ s:Zx¤(Ü¢éí=¯/³‘?•O±?ùƒÈSÿF¦™l´Éóc˜epú)ÅÉžÚÊØ1!Ç‰b�¢ÄØÆÿ©œ$7ruÞ;V´ðÜ0†ž(}Ñ&pzX;Ù‹ÔÓœ¨Ó2®”þXY‰š$òD¤2Ÿ¥1ô4“êt_Ý4ÈpÕMí+Âç2!’€I£à$NbyÄ“qî¬ÌÐØ�2Év\[NpÒ&ÿóiàçÏ ÿ$"ªù¢* Á�5@îÅÀ¤ÙQJè‰}BfÙÚc9_$LC†›/ÔÑøª'’oj·w2wµ¾•à…‰Ãƒšˆ©c`p 56c™UM�¸cð$fL"&HÇ˜´‹	&lêµŒ3dBDbcZ:à,#‰ ³[M[ :V V’>Nò·t²5°¡+ÓÎZUö6ˆ*w±CÈÇƒã³“4LÒ0¿[ýÝN¿/”i‰Œ´Èýo2ÓTŽd€¬F"³°~‘3Yæu“/~;¦Õƒ�	xÙu2‹63 Aš†¶.Ål	9+r0	bŠÎ¥×ñoÃYVˆ‰ókZ°! €_Á¦ÐÇÑQ’�ëÂð-Í$^·[¶^nóõšDJ›N�†kØø�û³(§„luÃ°+ýñ¦!&U‰…8d�§”Ñ&�@�Fä˜¼‹‰‚ Vo?JüàmšLã›µ$a®Óyáàá4¸÷ß$Y>íßIÂù²¿µwË&€-8õÁ›In[€+Nð•FóèAÈ�'÷†r2=ST�1pQ˜x¿'aìbZy1E£Ì»ó'¡Q’yÿ æH¦<î‰Ï_¨&r¿ªiƒ®/9yèdBP<ÂÓ/Â` ^ïüºóšÇ€êñ°œy.™Ã@Àø—6’'&)X€Fø½Úâç_ÕÞ[››øÇƒo®µÇ³«í¿nôÄæÎö6éÔV’U°VÓËX¹aÞ’èÝÈ°o)Ý»d-¨�Õ‹F*À²ªr�zøß'Óøp@Ü³m&\|—g†La_a'. S7cÃ`©‚Ø®šµ€Š
6}ä½‘\¯¨^‡Tì/d€·f$2ðTþI>GOÀèËù©üs†G×yw8Ä*§ó
•ö4JR™öyiŸeIÁå3Ž6o’%i Ù§SÏ8+ò/ì¨¡gŽò:`ð…ýÛÔ†W†®Ô–f·sã§â2	îÐ!™~F§ÅØ?¾ëv¨B'9�è�€‹¹·‡ižúíþ��#=}Fs U¾óÅsÇQâç;ÛËë}ž½ûJsu)Ô¨XT­º(º¾X£�‚LK»�©æ’ô×ïÈƒ¢]Ðs[œ�SæBuDE)¾´mâŒ`.Å¿û¯À�%qòéìA“àîIo6ËôŒõâjö7 là½—>ã�Q°ŸÄTg½¤(JöæO§Q8òó0‰UckV²0J×¬fmßlmªDø‰ŒZý%³ñô	ˆõ{6šZÈ´ÒÕm!]¸øA `‹“ßƒ™:¢,P‚:Wâ·øãÿQú‹5ÝÛ>yVåÜÛ›¯mÎM¯¨ïò°¹:¼@¹ÎRÉ†¬˜Õ/“w]dŒ°Â4~þð£™t•kQÊÁ GJ›£ÿTÈæñµc[Ío|{ŒùÕè®Öéh†¡çêD|Z«QG8�hÀ§WG³˜zÕùrœêë‚b%°j·�¢Žx68ÓZyn“RqI�Ùòy8†b\C×–ÇE†ÔesvÒè.&,$H´–¡Õf—ë7J£¥¶%4C~ÔlŒÞÅ}ºˆy(Ôµ[�Iˆ!¦‘„51f¹AlonÛ$�WíÀÚ¹Â±Çˆ|YÞ°
0›)K;:›³W•ßrŒ~ù!ê{–fÅk«Ì8%ØCn÷ûÙ§ãçJÀj¥ò=!%2kçS‹!½wÌFë6Ry
é—8m×nx|d}�Â•úÒ¸þøÄ©r-&Pç8¤ÿPFSÜÀP¢Þ.Ó÷Ðz/#ê²5­+Ñ:rŽ?þóÃÙððxxñ÷Ã]¼Úúuû7Êupí]â®t…Xþ4Ñ ™A|#ŠWx@KF'¤¤Y¼ü€{NëÛ[L{»Võ3M,[Jv/I";]ºÂaÊôÐà—Ö¶‡ì�Q¥¾OÀƒ¼J¹`³mRE&®ï&³[ñK™¹‰L¦hñR;Âñ�H“Ýz{ÕUéµ¦2R}'NeB·j×Þ´§†r
g´çÇÙ-5·ðJ—ÂoqÅÎåµ°æ©Pl±—Q?·P!ßX‡¬º�Êù"ýïdÅ÷M–T@j!§ðÞÎ–•¢‚èŠèEˆþ5´Šz‚î¶Ý9«—‡Ù4‰3ÉAô
Sñ3%ëç6†º½›ëŠÓÝX½æ,Û‡ô™µ6<õèÖ(Çs?Ÿe¾3H¾‘KêþÈ Ò¡þ�€Úÿ|í–óç fUÁ¦’º°ÌA/‘L5óö£$“üAÅ(
Q%“qñ0ýàq8ªiÈ… 4›öøËÌRó=ºhÃ‚óÓ#ø‚Sˆ[�Û¯ÐôITÀt<{³Fé�Ðâ“ˆzÓ†Ø2h�¯ª¾®Žˆ£PGL¸èçÔ¡©d;�µ7µÚ	©…aK*ôl#ñ9hfÕI*Öj^ë",M&k-oªÃ8ÌC ü�Ñ>€w;~œ¬×b«D»ÛÁ×3hw/”U+7‰zŽît;‚m•S†Wêé_éhÃûÅ^Ž“³4ú=¶Äº:1…ð°ßÅž×Œ·l1ä­k¼ºñóW˜ïb£ó,tQV¥�/ëo©ÀÐíÌøº¾n¦º;B ´´+c9où¾‹ìLÑ²÷ßu2Ð#Ìj¬w�®ûÞ-1»ªÔ^ÂÕø&c+7^»sÙa‰[Òû1C^„E¶fËf÷œÍ£sì &Të‚å€ãÒë´©7ÑO¿¯>Ñ0µnæ{fQ0C†bô
LZÌ¹ê¤ÊHæ¸³d´Ä,ÊúsË(úƒdƒúÔnO«;ÐÄIÖÓ]©#O@ýCå·E‹ z�&f{J°,„þ ³hÃ%À”!P}¬¥ä£¢à}÷ÿ	�%l”Cx óÿ�VÆU°n³ÅX&²Eªx* Õÿ100755 healthcheck.sh }ðR hõþ÷«Ÿ�<,viÃëO=Ñ<»_xûú#!/bin/sh
# healthcheck.sh -- Check explorer health and report per-chain block counts
#
# Usage: ./scripts/healthcheck.sh [base_url]
#   base_url defaults to http://localhost:8090

set -e

BASE="${1:-http://localhost:8090}"

printf "Checking %s ...\n\n" "$BASE"

# Health endpoint
printf "%-20s " "/health"
status=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/health" 2>/dev/null) || status="unreachable"
if [ "$status" = "200" ]; then
    printf "OK (200)\n"
else
    printf "FAIL (%s)\n" "$status"
    exit 1
fi

# Stats endpoint
printf "%-20s " "/v1/explorer/stats"
stats=$(curl -sf "$BASE/v1/explorer/stats" 2>/dev/null) || stats=""
if [ -z "$stats" ]; then
    printf "FAIL (no response)\n"
    exit 1
fi
printf "OK\n\n"

# Parse per-chain block counts from stats JSON
# Expected format: {"chains":[{"name":"...","chain_id":...,"block_count":...},...]}
printf "%-16s %-12s %s\n" "CHAIN" "CHAIN_ID" "BLOCKS"
printf "%-16s %-12s %s\n" "-----" "--------" "------"

echo "$stats" | \
    sed 's/},{/}\n{/g' | \
    sed -n 's/.*"name":"\([^"]*\)".*"chain_id":\([0-9]*\).*"block_count":\([0-9]*\).*/\1|\2|\3/p' | \
    while IFS='|' read -r name chain_id blocks; do
        printf "%-16s %-12s %s\n" "$name" "$chain_id" "$blocks"
    done

# If no chains parsed, show raw stats
if ! echo "$stats" | grep -q '"name"'; then
    printf "(single chain mode)\n"
    block_count=$(echo "$stats" | sed -n 's/.*"block_count":\([0-9]*\).*/\1/p')
    printf "%-16s %-12s %s\n" "default" "-" "${block_count:-unknown}"
fi

printf "\nDone.\n"
"ÖÊ!®xn ‘ÿ100644 icon.svg ^pý”`9«ƒ8(í=ëÙü0¸100644 index.html §Ož;ùÁ@’6‰w4²ÑLÏ100644 logo.svg ¿ÛÃž—ìÿ6Ñ•£„µ_|nÉ%€Ê¸,(¶
x¦ Yÿ<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#0b1220"/><path d="M16 44 L32 16 L48 44 Z" fill="#5cf"/></svg>
2.³xcœþ<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Explorer</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/svg+xml" href="/icon.svg" />
    <script src="/envs.js"></script>
  </head>
  <body>
    <div id="root">Loading explorerâ€¦</div>
  </body>
</html>
P{pÛ³xó ÿ<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 64"><rect width="200" height="64" fill="#0b1220"/><text x="20" y="42" fill="#5cf" font-family="ui-sans-serif,system-ui,sans-serif" font-weight="700" font-size="28">Explorer</text></svg>
iQL¶òxœµZéSÛÈÿŒÿŠYU$0rŽÚzUÞð^%@6ÙÂbRù�¢²²5¶õ,k„FÆ°Yÿï¯�]>³›G%`ÏÑÝÓýëcŽ4L‚‘Ó JZ­hšª,nkÏ¨$—�¹Ã ú�–}ãw™T%£ÎµJ°a8¥q±áŸDæ�qž§øYiü�ù¸3Œb‰°AçÌ§>ý”ðoM¥Ójí}Î(ÊÇ³¾?PÓÎ4Èó¤3RÇÀ:Êå+Qí�g�Ã¨3Ê‚tÜ‘É(J€Æ}�ÂG±nd”„òQfv¨ÎU¶v,v‚‚V36„:òaêlìLc•G�k?;¸àèµZ�Ž8ƒiz³Tf‘†‰jžh‘�¥€–ãv
3S	’YŒT¦f9(C‹4žÑh¤„�R„‘3Æ0>9X>Õ¢óð¢àÿMÇ³Ñ¢s(‚$¤V„m&Jò~&u”‰2PY&¹Ã”Xf¾¸AVZ kè‰¥ VÙ,A3wE"~D€´x	 ®+šs•Md¦ÛÀeªhœ4’�Œi=S¿•?¥rI;€¬ò­µ‡¨=‹¨h´ö¦3A?8ÿÄ{líé< �ÔÁwwH¬zØ&7:1Ëá©A*@lš¹C¨ûïyáPf¸X9¬ätø­g}Ý Êa0‹óFzø 4,RÖmu(ÜD	"›fr=z­E‹5S®£¢”Ái±ÖNU2Œ@1¬UaÞ?¥¯ïfÉ µ7•*ûDù¯ ¡9€4WrÞTþ “™ÖV4fJå2Dó‹ø­!_1ß­›Ì‡M°‚Læ³,Ï]Ðc-ÞµŒÚÐFî’‰'Ò]mg.Ìª.`ÔØ/J`vÌx«j½î‡ÒB%€÷G_Ü²ÇU»b¥&b–VÜÖx‹ÃLM‰I©¶mÜ•ÜS«ÂS4:ëÓ¸“èK"Ü%€èjs-0ZŽ§¸X¯Qº«—”êU×ì‚ôâ� ÙC´]Î=Ô:.Šê:Uü9mQE/¢ÆÕ¾]öåyËlDÜBáÜ»�£`ð´Š§ÍT~Å1žgLÜ“ùYÅÇÒh0� 3Žc‹ØÏÐ ³„ÝJ†·#ãlRj�‹K.jq
Õþtæ_¨ÁÄõðKÕéOÈŸÍ�OIÌƒXv $äg,M Ct½áÈB		<—IÞå‘<Ä€�ðé “`Ñjºj¢ŽUºi%DÉÅÈQ	Ë‹ˆ†âk[ Æ»° ŸüîJ…kºû;ÐGë‹Ú3îŒþ·7ÈÛ&Ä#�>˜9&¹¶é--#°@º	�ãøg¥C¾]T‰¥×5(sC4ÔwNâc“±™xöL4¬r"Ç,¡n-K‰Q_ÊæÏGþ›0t_€¤#%PÏ.i#½Ä`‡#ÎT"I!Ú+‘¢]VÈpÍ"fd$8ûç	úzH” |=BEdA]9°gòŒ¦¡Hä'=ªMßÛCÍFÉLâ—þbRzDŸkkØk¬bÍ:ÌJ,ób1(%õ/hØ¢½P÷¤EŠÇ;°ÀµA.ÐÞ—±ÂJo„Å"y3vUºÅ÷ò&h)«“²ÚÚPÆ2—®™Û&�Ø–àCb¬�ã¬ƒkƒÅDíbæ>¿V™ lYæ®ýJâß \²Ø¤-¾‚jŸ<ÑW*&a@Ò‰|²:˜ø®Qy,z—êYõ5…B—†#§#¨cê�4c`›³¡
€- v
þ5ÐÍ‡®ó¥Lzwd¯P¹¯«K®\ôQ7Z`Fƒz%Ä àÚ«ËòP>ÈìÉÄòŽ�P¥6ü„Ú Ò8²*Ê#þKë~}=äë°Ù5j”êŽ/w¬Æ¶xÞ±L,^0z!¾'¥ßš
9èb}mTë\Š€�*‰Æ\á
^ér6Ôà,^LÕx ‹-èŠ‹ìAmƒ‰†×o5ê•�Ô¨Öà¼	kHÂ FŠ6Ó‹7×€Å@Ò€ÞïV¦PM´#e¨M–¨†Å¦5(²,¥'ZÖ“ »ûôSªß–ˆEôf§•�VÚ¿œ„Qö&†¤c¨€íþõóÏà8æ§‘Dì@5,ïë;1Å™]±ÿà”ÔÛ8«:pŸ'Û–¥ÙúPJ¢a$C×6™œdšy�…X^)ø&¡Ì¦r±l4áüÓXiéÖTÃv‹Ôa8¢¢|öýk0Ó²ÉJ38°÷Ìž€Œc@ê‡}§T�3uc¢Ø×ÂÝ×ž8þ7–’?~º
¦’?�£ÞÓ´¯â¶`90ÖBº_Cl›z€\Igå@u7‘@éw`Á@Kˆï +ta©©Š©¼yõ\
,ß¡Ð4‡Šq®áâ_ÃÈ ïì!ˆQÃ¦üÀaa¢pdZžÍ² �Ô]GÃØê¤´•‘æD„6ïã€]A¤FDr.~eüÑtr8Tn°J]]Îº¼ç&årõÞ\Ÿž'a
êHP/´PÏûÛÛëk•å<ñ9µUÑ(26/ÚŒE�5×€á2@vÕÖÁ°À!ëaXñÍˆ_Çj=Ü7ñ¹™%Ël c¦9Ï2(ºN¶±†H¹Žó¢8_è®òÕ}]ª(´Ž$0�óëT D„	a
Ûœ·äQˆ~äVÏ�6é4\©RæÕOµê)‹=™ç±IöH(�õãH�åæ””°»yWÙ^oÊ:+ü~kò±1¡ZsiÄåÚ¹¾…G•-Di0†ê¤ðí+5w=*¾¶ã*õ\šœOÉê->ßá€SÕ{·†7ÓMdÞÁ¥ÜJ¢±‘_Û•\ˆ›*×Ä¹eøÃèª¤DÕ.Ãn–�ú ÃcxÆeìë%È2ÃÒiJÝì ³�©ìke„¥èÌº¿þª·`˜f™Ã~¡}û¡s{ÓS×9b)�œÿLU(O2åxF'K±ö!ÈžÕæôí+†Ý¾ÿ;æ²XˆÓ;¿8?½§?]Ýº‡žxwóñR0¿¯Ó@ƒVÅç÷ç7çO÷NèPõ@¼¹:	Ý“�ü+ÈŽç÷ >sŸ%Fqý2“³p	ŠöÜ¶“w°a)�S««ö¾ßxÆ|õ�)·Ù4¥³‡2M§Æ\Õ+â �`AÒ²�•Ìe<úì-¯k¡¶œ¸–2W=å5r^Q>ù°RQàˆ xv(Ÿ­Id×½Ö&ŸàGÍíg±[²ÛØ£8:¡Y�Š­|”¹¯;‡5aÕÔËÃbÛbÏìYX`®>Ê«¾âã¡£±,ŽÜqÛRÏ ˜‚Ìy,ÊS¹ö0¹�.?¾YÇès‹Î–ýJõðc‡¬¡GÂÎ Ã†;n] Ñ$Ü23×nfÂ�÷1�õ[mkæm{šâvýyï²WélÚ®l`ýc÷-�­[—õ¢Ð%_±~,êpù¶Ê-ªÖú1Ú¬;ÅægE©Û{aÁ_(”’Û�ÄÆ˜ÏLS>/ƒGÊ[g2Åè)^P�­7Rã±Vô'†Çâõkñ’ºhô-—�é_=‡Ÿ6Ÿ™úŽN!má>?ãöÊ¹«iN0�ò‘Y©4�Ñ¿PAøvÅ9º	ÞÑÌLxÿ~½²xÈJ[§EY7œæ~/5ÄËB\V‘åÛœIw˜»Ì�Bwt›J´)sZNU¢åçjðçÌÜØÜð­,ã)óßªðIÚ`›·O©o ôCLžÃ$êoƒí^¿~ù…õI&ï-µ4ñÂå<ƒzé0Ïçïî3˜¾¢@$	 µŠÿñÍ‘øY;Ý/ßœ©Ô/»»°™‡í]Šßz¯œÅÝâs¥ƒÅçL¿B»¼¥Tú°8”HInfã¬ë‚Q.8÷ßóú¡>’¹ëÐ�$?ÆêKº Mãh@›~Û€“ìšÏ^óÜóù£‹|ÍYÉøOÌõ·Dã‹Œ¬$ªï…ï
YÝ,¨XMœ¶CšÓÝÛæü$Ä/¨n�ûx›¿Å!ô�ë¤ðüdÑ¼ÖòŽnÙœë�½[á±9úû„.ì-Oûõ|Í,´"¤ë‹è÷‹³›L¿›ßî¬Ç2ˆsJµU›ó™|õ¼^áQùQMªW•xãê-Ÿ^Q`âZ¥^&}3šjÃ?–e!L´;Ù×°Ç]Þr cùm Cqk›s®«÷¶¼EÞò8¤òÁœ,WÕž}²tåm¯ÖaUý§æ{SQÕ�ð{\L“BÀgËp)¾�ÍüO7>o7ë›s¯qDwÃæ@–Â(Lp•L :C¼Ÿä{¥z±ŒiŠâ1ƒ�ôØw«°ðøþ�ÃˆM³ÎZ%HFÑÑ\¦Uº±žYˆ·jn´YWÖfMcÒ›†ê!qÈðèÞ–‚PI³®µs|–0G«ØGH hd%ƒxò] ÝYô%H‰lc9
¶½X¨/~'°zÖ^ÂrÙV•«¹¥;<y…qeI²:³A¤M”U kÏi¤³+•¿CÓ7Ì:.ï"W`(Öá¸–=;[¢r¾VirGhEù)	‚(Æƒ„†ÙKºCÂ::©åRû
g+Ž²PAÅ'swx›ESsyÈþç�cm�üËfXª?Á�Vy©¡b_íiI«ñHŠžJÄ‘ä{ª _¬Aj;ÈÅ4‚¸Š lV—´K0ªËõ‹Iå“™(°€Z,vFL³�¶‘ý ©¿ƒû±(¡T^M]µç‹ÅqaBÝ§á!–©|w091ùKSð¡mèòb›†6OšØ"4ñõ\(t'ë¤A–ë*Ç"ëªTm[¼bxM£=ûµÝ¨OäÒ¦i_žß¶ä†wµPV-‹o0û{!¼-;š¬g½Gð\æU6›ëj|ZD”®Ô�KTl€‡kü*u’¨yb‡p¥?ò›…�/n59±–"ªÐ
À"0Ýx d¸‹	š{tzÎ—Ô^±”þŸ Ì›¶Ü2bû¤ tˆ¯9fô¸géÅF•”Ùò‹º™„ÿ$>)dŠ×à´F…XÑà2®%º‹8ß`ñ¥®@vH`Ó
jëÑ×"m‹øu8ƒh/ªã-žež£´–{-ŠÍ˜EëÙ 7¦x& Ùÿ100644 docker.yml ­3â+	ë«œÛÊ³’Xi¼…ŠÃŠ]5ã‚Ë7xœ5Œ1
Â0FQ°BGApü@p³7D'”â›4&ù%IQ'Ñ+®‚rsòÆÁñ½�ïÝ{·ÞãÓTÌCÙ‚¬W>P°¢RV6ídyÝ)¬p,HÊ|mP:2•H‡8::_àÃo=	°"ÔLëhD„€}­4GP&’åðuD,d’4³2#'³t“¯àÆå??a±ÞæËùlBeÙ�†88�¬&ÆÓß£y%»ç´ûn}à¥D¨xH ·ÿ100644 ci.yml êþÿòÅiæöVäPôy˜� sÌ100644 docker.yml ­3â+	ë«œÛÊ³’Xi¼…ŠÃŠWa L·Pxøúname: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

# go.mod uses local replace directives to ../indexer and ../graph. CI checks
# out all three repos as siblings so `go build ./...` resolves without any
# network hop to a tagged version.
jobs:
  test:
    runs-on: lux-build-linux-amd64
    steps:
    - uses: actions/checkout@v4
      with: { path: explorer }
    - uses: actions/checkout@v4
      with: { repository: luxfi/indexer, ref: main, path: indexer }
    - uses: actions/checkout@v4
      with: { repository: luxfi/graph, ref: main, path: graph }
    - name: Verify build deps
      run: |
        command -v gcc || { echo "gcc not installed on runner"; exit 1; }
        gcc --version | head -1
    - uses: actions/setup-go@v5
      with: { go-version-file: explorer/go.mod }
    - name: Refresh go.sum (proxy cache inconsistency)
      working-directory: explorer
      run: rm -f go.sum && GOSUMDB=off go mod download
      env:
        GOSUMDB: "off"
    - name: Build
      working-directory: explorer
      env:
        GOSUMDB: "off"
      run: go build ./...
    - name: Test
      working-directory: explorer
      env:
        CGO_ENABLED: "1"
        GOSUMDB: "off"
      run: go test -race -count=1 ./...
C‘Œd¨xH ·ÿ100644 ci.yml Bñ©‘Ž7Óêƒ[2t«w‰ô×è^"100644 docker.yml ­3â+	ë«œÛÊ³’Xi¼…ŠÃŠ!ä	‰ix” kÿ‡
Á	°uInstall build deps
      run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends gcc libsqlite3-dev
   ³:“]q“å"™è1¬k�x ôÿ¤˜°£$>  x0 Ïÿ100644 scheduled_tasks.lock �Sã»îg	Õ«³FÙÝit)™š®ºxZ ¥ÿ{"sessionId":"cf4e8ffa-52d2-4e07-b22c-dbf44cba2479","pid":5284,"acquiredAt":1778041169393}Êï¯g‚æx øÿ±¨°(
›7¨xH ·ÿ100644 ci.yml Ò5TÁØ·´µ¼¼[¹jx6100644 docker.yml ­3â+	ë«œÛÊ³’Xi¼…ŠÃŠ¯Pìj‚Kx
 õÿÁ	�°N’ÁÆºo�]x ðÿ¤�°Ú“ã5£$1ífì‚¬xìþ»/¾�š³º`“´zmainnet“-A<
    indexer:
      poll_interval: 30s
      start_block: 0
“‘¶“ÿ“^7    “¯a    logo_url: /logo.svg

    tokens:
      featured: []
      allow_list: []
      block_list: []“emAmainnet/ext/bc/2Y625Kvdd4wZ5LKx9kLSf1jGALyARmXVN1YsTb4F3Hzv4bPDbp³ÜhAmainnet/ext/bc/2GiQb73CeJESjc4omFv2YtQHZrRgJf25NXPzAr5J6UNHRcDV2F³Ph@mainnet/ext/bc/rtjwvtE1tEvrokmpeYdTq7b2zqZgmybKwR5MLjKMGAR1W78dQ“Â¹Amainnet/ext/bc/2pUskxqaL5Bpx7uRUGG1fDjPckjxQ4UKX4sLKeaS1NdSVBJd3F“†K,¶¡¨xH ·ÿ100644 ci.yml ïÅê$¨ÏJà˜«¬qvT¢-xÇ100644 docker.yml ­3â+	ë«œÛÊ³’Xi¼…ŠÃŠ°”n‡Qx ñÿÁ	‡°o“÷W’Á*=´é�Ò}x9 Æÿœþœþ°0*5Rl34569IHJ5hXOvrkjbadfKNt/2rGZd83vhcSIRSP³10ëNI“¯xO °ÿ100644 actionlint.yaml £…Àj @\/B|öÈV5ƒÇÎñé40000 workflows nÊ9w�Õ~ÿÐ4YQ_ïí‡3ºa²�!é\x æÿOO�;_,�ïêýtÏ ]£§£¦Aá¦Ž‰©¹xy †ÿself-hosted-runner:
  labels:
    - hanzo-build-linux-amd64
    - hanzo-build-linux-arm64
    - hanzo-deploy-linux-amd64
c&ú§xw ˆÿ100644 ci.yml ü0è5�-FW,ÏòRo±RG�)ó100644 docker.yml bU/ÖÑ>4qÍÜZþˆ�Xq/åì100644 workflow-sanity.yml ~¨Žzêƒ‰2–/V)(ßõ„Á>o¯.Íì€x ãÿww�4-0V7óY=¦,±x»Äëå´]ƒ¬ê‘H/²ö¶î+xN ±ÿwwH100644 ci.yml mÌ�ã£Ò°’Õ1£IeiVƒZÎÂ›100644 docker.yml ã÷Éè‡Ç…Ü_amþ1}ó79%‘H/ô!nè\x( ×ÿww"100644 ci.yml Ñ+_È-Â‚äýhu….¡ã@�‘"USã„/x3 Ìÿ‡ù°Ehanzo³H±	|
       “øIrun: |
       “i�£Ü¾%x^¡ýname: Docker
on:
  workflow_dispatch:
  push:
    tags: ['v*']

permissions:
  contents: read
  packages: write

# Multi-arch (amd64 + arm64) on native hanzo ARC runners.
# No QEMU emulation. No GitHub-hosted fallback.
# Semver-only tags (v*); see hanzoai/.github docker-build.yml.
jobs:
  docker:
    uses: hanzoai/.github/.github/workflows/docker-build.yml@main
    with:
      image: ghcr.io/luxfi/explorer
      platforms: linux/amd64,linux/arm64
      runner-amd64: hanzo-build-linux-amd64
      runner-arm64: hanzo-build-linux-arm64
      runner-deploy: hanzo-deploy-linux-amd64
    secrets: inherit
+qÇ½	x� bÿname: Workflow Sanity
on:
  pull_request:
  push:
    branches: [main]

jobs:
  sanity:
    uses: hanzoai/.github/.github/workflows/workflow-sanity.yml@main
2§5å·)x—hýname: Release
on:
  push:
    tags: ['v*']
jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          platforms: linux/amd64,linux/arm64
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.ref_name }}
            ghcr.io/${{ github.repository }}:latest
ƒ¯Çaï„$x/Ðþ—Ì[name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  test‘:!‘—7‘¬setup-go@v5“go-version-file: go.mod
      - run: go mod download
      - run: go test ./... && go build ./...
  docker:
    needs: test
   $ if: github.ref == 'refs/heads/main'±;main“.“�ú\Mà�>x` ŸÿÌÇ�"
    - main‘)
    - main‘N#hanzo-build-linux-amd64
    steps:
“ç$‘«"with:
‘×$‘ý“'HPà»Ïxû$Û# Explorer

Combined block explorer: chain indexer + subgraph engine + frontend in a single image.

```
ghcr.io/luxfi/indexer   -- chain data: blocks, txs, tokens, contracts (SQLite)
ghcr.io/luxfi/graph     -- subgraph events: swaps, pools, DeFi, GraphQL (SQLite)
ghcr.io/luxfi/explore   -- Next.js frontend
ghcr.io/luxfi/explorer  -- all three combined (this image)
```

Each component runs standalone for independent scaling, or together in one container.

## Architecture

```
                          +------------------+
                          |   Load Balancer  |
                          |  (hanzo/ingress) |
                          +--------+---------+
                                   |
                          +--------v---------+
                          |    explorer:8090  |
                          |   (single binary) |
                          +--+-----+------+--+
                             |     |      |
               +-------------+  +--+--+  +-------------+
               |                |     |                 |
        +------v------+  +-----v-+  +v-----------+     |
        |   indexer    |  | graph |  |   explore   |    |
        | /v1/explorer |  | /gql  |  | /* (static) |    |
        +------+------+  +---+---+  +-------------+    |
               |              |                         |
        +------v--------------v------+          +-------v-------+
        |  SQLite per-chain (WAL)    |          |  ZapDB KV     |
        |  /data/{chain}/index.db    |          |  fast lookups |
        +----------------------------+          +---------------+
               |
        +------v-----------------------+
        |  Replicate -> S3 (optional)  |
        |  E2E PQ encrypted (ML-KEM)   |
        +------------------------------+

    RPC Sources (per network):
    +-----------------------------------------------+
    | api.lux.network/{mainnet,testnet,devnet}      |
    |   /ext/bc/C/rpc           C-Chain             |
    |   /ext/bc/{blockchain_id}/rpc  Subnet chains  |
    +-----------------------------------------------+
```

## Quick Start (Local Dev)

```bash
# Clone
git clone https://github.com/luxfi/explorer-combined.git
cd explorer-combined

# Run against testnet C-Chain
docker compose up

# Or run the image directly
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=https://api.lux.network/testnet/ext/bc/C/rpc \
  -v explorer-data:/data \
  ghcr.io/luxfi/explorer
```

Open http://localhost:8090 to see the explorer frontend.

## Run Modes

```bash
# Combined (default) -- indexes chain + subgraphs, serves API + frontend
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=http://node:9650/ext/bc/C/rpc \
  -v data:/data \
  ghcr.io/luxfi/explorer

# Indexer only (chain data API)
docker run -p 8090:8090 \
  -v data:/data \
  ghcr.io/luxfi/explorer \
  indexer --chain cchain --rpc http://node:9650/ext/bc/C/rpc

# Graph only (subgraph GraphQL)
docker run -p 4000:4000 \
  -v data:/data \
  ghcr.io/luxfi/explorer \
  graph --rpc http://node:9650/ext/bc/C/rpc
```

## Multi-Chain Configuration

Use `chains.yaml` to index multiple chains in one instance.
See `chains.example.yaml` for a complete template with all Lux networks.

```bash
docker run -p 8090:8090 \
  -v ./chains.yaml:/etc/explorer/chains.yaml \
  -v data:/data \
  ghcr.io/luxfi/explorer --config /etc/explorer/chains.yaml
```

## Per-Network Deployment

### Mainnet

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=https://api.lux.network/mainnet/ext/bc/C/rpc \
  -e NETWORK=mainnet \
  -e CHAIN_ID=96369 \
  -v data:/data \
  ghcr.io/luxfi/explorer:v0.1.0
```

Mainnet chains and their RPC endpoints:

| Chain   | EVM Chain ID | RPC Endpoint                                                         |
|---------|-------------|----------------------------------------------------------------------|
| C-Chain | 96369       | `https://api.lux.network/mainnet/ext/bc/C/rpc`                      |
| Zoo     | 200200      | `https://api.lux.network/mainnet/ext/bc/2Y625Kvdd.../rpc`           |
| Hanzo   | 36963       | `https://api.lux.network/mainnet/ext/bc/2GiQb73Ce.../rpc`           |
| SPC     | 36911       | `https://api.lux.network/mainnet/ext/bc/rtjwvtE1t.../rpc`           |
| Pars    | 494949      | `https://api.lux.network/mainnet/ext/bc/2pUskxqaL.../rpc`           |

### Testnet

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=https://api.lux.network/testnet/ext/bc/C/rpc \
  -e NETWORK=testnet \
  -e CHAIN_ID=96368 \
  -v data:/data \
  ghcr.io/luxfi/explorer:v0.1.0
```

### Devnet

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=https://api.lux.network/devnet/ext/bc/C/rpc \
  -e NETWORK=devnet \
  -e CHAIN_ID=96370 \
  -v data:/data \
  ghcr.io/luxfi/explorer:v0.1.0
```

## White-Label

Override branding via environment variables. No code changes needed.

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=http://node:8545 \
  -e CHAIN_NAME="My Chain" \
  -e COIN_SYMBOL=MYC \
  -e COIN_DECIMALS=18 \
  -e LOGO_URL=https://example.com/logo.svg \
  -e FAVICON_URL=https://example.com/favicon.ico \
  -e BRAND_COLOR="#FF6600" \
  ghcr.io/luxfi/explorer
```

| Variable       | Default       | Description                      |
|---------------|---------------|----------------------------------|
| CHAIN_NAME    | Lux C-Chain   | Display name in header/title     |
| COIN_SYMBOL   | LUX           | Native coin ticker               |
| COIN_DECIMALS | 18            | Native coin decimal places       |
| LOGO_URL      | (Lux logo)    | URL or path to header logo       |
| FAVICON_URL   | (Lux favicon) | URL or path to favicon           |
| BRAND_COLOR   | #1a1a2e       | Primary brand color (hex)        |

White-label detection in production uses hostname-based routing.
Each `*.lux.network` subdomain resolves to the correct chain branding automatically.

## PQ Encrypted Backup

The explorer supports post-quantum encrypted backups to S3-compatible storage using ML-KEM-768.

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=https://api.lux.network/mainnet/ext/bc/C/rpc \
  -e BACKUP_ENABLED=true \
  -e BACKUP_S3_ENDPOINT=https://s3.lux.network \
  -e BACKUP_S3_BUCKET=explorer-backups \
  -e BACKUP_S3_ACCESS_KEY=<from-kms> \
  -e BACKUP_S3_SECRET_KEY=<from-kms> \
  -e BACKUP_INTERVAL=6h \
  -e BACKUP_PQ_ENCRYPT=true \
  -e BACKUP_PQ_PUBLIC_KEY=/etc/explorer/backup.pub \
  -v ./backup.pub:/etc/explorer/backup.pub:ro \
  -v data:/data \
  ghcr.io/luxfi/explorer
```

Backup flow:
1. SQLite checkpoint (WAL flush)
2. Snapshot all chain databases
3. Encrypt with ML-KEM-768 (post-quantum KEM + AES-256-GCM)
4. Upload to S3 with content-addressed naming
5. Prune old snapshots per retention policy

Store secrets in KMS (kms.hanzo.ai), never in environment files committed to git.

## Performance (LP-104)

Benchmarked against Blockscout (Elixir) on identical hardware (4 vCPU, 8 GB RAM).

| Metric                    | Explorer (Go+SQLite) | Blockscout (Elixir+PG) |
|--------------------------|---------------------|------------------------|
| Block indexing (blocks/s) | 850                 | 120                    |
| API latency p50 (ms)      | 2.1                 | 18                     |
| API latency p99 (ms)      | 8.4                 | 145                    |
| Memory at 1M blocks (MB)  | 180                 | 2400                   |
| Disk at 1M blocks (GB)    | 1.2                 | 28                     |
| Cold start to serving (s) | 0.8                 | 45                     |
| Concurrent requests (rps) | 12,000              | 800                    |

### Cost Comparison (monthly, per chain)

| Setup               | Explorer        | Blockscout         |
|--------------------|-----------------|--------------------|
| Compute            | 1 vCPU / 512 MB | 4 vCPU / 8 GB      |
| Database           | Embedded SQLite | PostgreSQL 2 vCPU  |
| Storage            | 2 GB SSD        | 30 GB SSD          |
| Estimated cost/mo  | ~$5             | ~$80               |
| 5 chains x 3 nets | ~$75/mo         | ~$1,200/mo         |

## Endpoints

| Path               | Description                        |
|--------------------|------------------------------------|
| `/health`          | Health check (200 if indexing)      |
| `/v1/explorer/*`   | Chain data REST API                 |
| `/v1/explorer/stats` | Block count, tx count, sync status |
| `/graphql`         | Subgraph-compatible GraphQL         |
| `/*`               | Embedded frontend (static)          |

## Environment Variables

| Variable         | Default    | Description                              |
|-----------------|------------|------------------------------------------|
| RPC_ENDPOINT    | (required) | EVM JSON-RPC URL                         |
| HTTP_ADDR       | :8090      | Listen address                           |
| DATA_DIR        | /data      | Database storage directory               |
| NETWORK         | mainnet    | Network name (mainnet/testnet/devnet)    |
| CHAIN_ID        | (auto)     | EVM chain ID override                    |
| LOG_LEVEL       | info       | Log level (debug/info/warn/error)        |

## Related

- [`luxfi/indexer`](https://github.com/luxfi/indexer) -- Go chain indexer
- [`luxfi/graph`](https://github.com/luxfi/graph) -- Subgraph engine, Graph Node replacement
- [`luxfi/explore`](https://github.com/luxfi/explore) -- Next.js frontend
- [`luxfi/explorer-v1`](https://github.com/luxfi/explorer-v1) -- Legacy Elixir stack (archived)
©�3»°xôô# chains.example.yaml -- All Lux chains across all networks
#
# Copy to chains.yaml and adjust as needed:
#   cp chains.example.yaml chains.yaml

# Mainnet (network-id=1)
mainnet:
  cchain:
    name: "Lux C-Chain"
    chain_id: 96369
    rpc: "https://api.lux.network/mainnet/ext/bc/C/rpc"
    coin_symbol: "LUX"

  zoo:
    name: "Zoo"
    chain_id: 200200
    rpc: "https://api.lux.network/mainnet/ext/bc/2Y625Kvdd4wZ5LKx9kLSf1jGALyARmXVN1YsTb4F3Hzv4bPDbp/rpc"
    coin_symbol: "ZOO"

  hanzo:
    name: "Hanzo"
    chain_id: 36963
    rpc: "https://api.lux.network/mainnet/ext/bc/2GiQb73CeJESjc4omFv2YtQHZrRgJf25NXPzAr5J6UNHRcDV2F/rpc"
    coin_symbol: "HANZO"

  spc:
    name: "SPC"
    chain_id: 36911
    rpc: "https://api.lux.network/mainnet/ext/bc/rtjwvtE1tEvrokmpeYdTq7b2zqZgmybKwR5MLjKMGAR1W78dQ/rpc"
    coin_symbol: "SPC"

  pars:
    name: "Pars"
    chain_id: 494949
    rpc: "https://api.lux.network/mainnet/ext/bc/2pUskxqaL5Bpx7uRUGG1fDjPckjxQ4UKX4sLKeaS1NdSVBJd3F/rpc"
    coin_symbol: "PARS"

# Testnet (network-id=2)
testnet:
  cchain:
    name: "Lux C-Chain (Testnet)"
    chain_id: 96368
    rpc: "https://api.lux.network/testnet/ext/bc/C/rpc"
    coin_symbol: "LUX"

  zoo:
    name: "Zoo (Testnet)"
    chain_id: 200201
    rpc: "https://api.lux.network/testnet/ext/bc/2Uw1b5tf63vamHsWVeSW9uHcGBVbF5CYHS9JiLWttusY4Vvjsn/rpc"
    coin_symbol: "ZOO"

  hanzo:
    name: "Hanzo (Testnet)"
    chain_id: 36964
    rpc: "https://api.lux.network/testnet/ext/bc/2wbYEFh7ELuovqXhyYeCLvweZrcEgNUoZVJUAtw145qTVAUJxE/rpc"
    coin_symbol: "HANZO"

  spc:
    name: "SPC (Testnet)"
    chain_id: 36910
    rpc: "https://api.lux.network/testnet/ext/bc/u4pC2tT61o83pbQ8qwAyfXXGF6nyGFNSwCYHEFcUeESGiNKfu/rpc"
    coin_symbol: "SPC"

  pars:
    name: "Pars (Testnet)"
    chain_id: 7071
    rpc: "https://api.lux.network/testnet/ext/bc/is5GUzsKHk3Z4Q5JXTjF4Ld9p4TnEs81tJFdaBiEBJVkaFrrs/rpc"
    coin_symbol: "PARS"

# Devnet (network-id=3)
devnet:
  cchain:
    name: "Lux C-Chain (Devnet)"
    chain_id: 96370
    rpc: "https://api.lux.network/devnet/ext/bc/C/rpc"
    coin_symbol: "LUX"

  zoo:
    name: "Zoo (Devnet)"
    chain_id: 200202
    rpc: "https://api.lux.network/devnet/ext/bc/2caKCdNop9C4oAPR1kYihN53HeSRDXD3JyMKS7ntfcpQYucmii/rpc"
    coin_symbol: "ZOO"

  hanzo:
    name: "Hanzo (Devnet)"
    chain_id: 36964
    rpc: "https://api.lux.network/devnet/ext/bc/tecXMucYDxwN65mebPE6cvQ9GynG6Y5WEvwGFsY3xVDWHxiqT/rpc"
    coin_symbol: "HANZO"

  spc:
    name: "SPC (Devnet)"
    chain_id: 36912
    rpc: "https://api.lux.network/devnet/ext/bc/213ZsFLRCBSmrii4huQqa6S3SxYPP8ec6dPEn6ad4CtLk3RdXp/rpc"
    coin_symbol: "SPC"

  pars:
    name: "Pars (Devnet)"
    chain_id: 494951
    rpc: "https://api.lux.network/devnet/ext/bc/MEGAgimfcodi7QaqCruFpTkNM6fqu7ZduLmWJJjGQwUg8uDPJ/rpc"
    coin_symbol: "PARS"
×‰jÂê‚Ñxœ›(¿€e‚‡ni^fZfjÊD…œ‰/ø&«3ªNžÃè1ù(³áäóüLfF“×óJsææ§(”èZLNàUa22R²œÅ•yÉ Q¯Ä12ñdJòós |=}}…Ì¼”Ì¢Ôä.M. 5$`î5ôRxœ]Î;¯›f Æq�iÔ¤ª²$Qn=;�¹Øs”¨²æb›‹1`{9áfÀ¼€Á˜Û’-S”åÛ¹c—Ó©CU© j·~ƒ.ý
Ub%‘NûþÏïùáß³wO ýèOør^>ë\=zæžÁŸ¨›ð¯ï¾‡oø¯!Ç/q’8ðGÈGv‚£u‘‘Ò	po’�6Ši `!×ô¬v8_Pá+þñ€$P?íÄ©{Šã’ŽP´1bçÔ|™
Ó,ò�Å`2çEšYJ’)KæÊ–TˆËOáù5üÝú¾s¿€çnÜÅì´ÓGh Ùl«h®ñ]¢\+£ŠÝ¯ÓŠ±ç}Bwéz¤—°1ø›{ïâÔ]»,íxt�Iñ Ú‰^°Äœõ†¥T3êÕz¦Lò…/"”Õíã«o½Ï‚údO»ÒÎ©0DC—{n¬¹NmÊk^Ö£®kH³’�I9l ²½qÊ®Ñ.bJ˜Rêr>ÔÇU‰–‰æÈ½*áU¹ïèm‚.š‚…/ƒûðv ðOðú Ä[m”*´ª¬gXÝLE%Ã|Ç]®›IšÕKÓÞªG^èÿºã¶@ªé÷Ç;aô"Áv\_5Ê½Hæ­‘å}kÕ´›«Ûþ~sõ0œu	â£¼*Šl:N*}ÖŠ³,*4Õ¦» �2•†²’5ouÙ2…ñ½‹SwÍF=ðö¡%ÎU}ßCR¥69ÎÝØ"�åLXïdy9Ù(º3„¿Fàóˆ‡¿€ã«Ï¼ŸiÜl“•ÈP � ¶I(bÉ†Yp}n¤´aoJ-Ù_|é§ÀJüNšûh�i
ç%Öùß#k\9‡]VfÔ:+È¥Ô¬(u®^b«(W`vJF¹vÓcóqàÏñÞ-ô6ÁØð›q/Å=clÅPž†åˆÆ¤$ÝµE`¸ pëMçð«¢‚wÊ·ïAþ+±€xœÍWÛnã6}¶¿‚�…8RÒ¢‹ÖMÇ¹ ¹Áö"‹‚–h[�LªåÄ]èGôû%�RŠ{·é[�@É™3Ã¹+ŠØ�ø\°%O%Kf‚•2�¥"aâ)Ï”š	iôšå*•&ìFü±Q)Mº{ËÒði&X¼@ -æi�´�©Y°åéÍ˜%i«•€=.v/¦c?¤<C„
ð¤L³¤ÏÎoïoG?©ÙŒÍ›â&ÛS/ª„NxŸ±—Í½½XÉY:·Záš/3¤s¿Ý2‘ÅÑŒg…€E®´a?ìÿ¸ïTÊUß²ÝÝŽ&¬ùûûÏ¿ØÅdrÇˆÇOÄŒ—™e,Ëð—»«ÛÑpôëàöæìòÜ²äÌ`Ô¦^5õ5ÚÈ	’LùÊlµ0£Ká„.Ž/-_C¿Tf©¬<‘�úÌ‹iÕ_“÷£(ÃÞJ5–ž»úq²¦ã»Kg€ó!Ý?ZD•õ"Ž4‘½JS.Šr7¬¬7ž¼•™'‰åu¬¾.7ú\dåü™XË<áF4¹O‡WÃÉðmÜZ,Á¾57…•Igƒûq©ŠXb/Êiët*Ð½ÓbºˆŒæ²à±IÜ1‡x³ày#ÅºÝtiC¨Ûñ h�x2¼
«$•óè·BIÜ˜e|Nÿ—tž)ZIa"t¾«Â>£"�Kžá¢X1ÏèõºA·;+eL¢ý€}îvHxÿˆ!~x)�ïáŽ×ƒà[ÑC–Ù=æÕAïÝŽÍ²³‚´‚jûž=²8¾÷*`Ûó­‘”j£žò =Q*ó=ÌUh×­œ�Ê å¥Q{U¾0pÏ\8²UÊ)�P!ßq]ÌÒ‰ÍS�Å\Æ"C¹Îá=®íúÕÖ	øo®U)? HGt­¥A$°ÿ`�Kþ |�,™*Â1y¥Ç¢€÷ðF™t¶ö‰¾Çœ³ÂñåùåÍ¤µžG×À½g×9Ü#Fxƒ`ïÀì&“¾÷±ŠÏO¬X”&Q�’Yyâ±HW"ÁÛwj…;Ï¤5„ø ]³}mk:s5Hë3¸Ý�x$†‘Û«P®OØL«%sExñÑí¤3¶Ûˆ—oŽ èp ´FÄ
=Dˆ3@@J¿ÁüD¤À+ÓŒ˜éîgÜðlÖººåé³�FŒÖxãg¸êk!˜H5 Å«­§6Lª_TÞP:…o©‚ZÈØpH­vñ¶fhD8¢�[k@bÂ“ÓŠÅ�X—�E9í¶˜a#„VèC(;_P¶.Ë§nx;,!xn,ôJ\—O•Ó.tAdá4æLœa¤yX÷£‚5(ú-ÈH9Ô5q¯S#t�i¶ëö/EalŒ>† œíáX@1`
I³7Yç­Ëó<KcŽÒV:ô¾ ŽC¬�Àû„öÕ_òücAæ—kò¿WnÊÂƒÞ¦¼mYOÀVm™d«ñ<~F{º[×]îÿRß ÕkhËt‹`…:ß€9N’m(¾âzØ&Öj‡[àÞØ7GÔ"�íªæø2ÂmJÚÚ.›¸±¶=}�Žî-Þàž
="†ÿA¬¶î8F¥¨[PèÕ»]´pp¢›õwæï¸I¨è1SwVè±‹Í··ÚÍ²B]H¯0áßÑE)Ý5Þ"L÷[£,ƒ‘"çN�\	¡t±Fo±€/ð\”X‹MÀîª„ñò`Ÿí2âÇ*pR‘Õö÷Ý2rM›îý²KPæÚÁ·[è®ù“ÕídmDA¤ìð�¼ïÙrÚî¥/=Ì^‘ŽeBfóÛ]çÝ»jE–jm�;ÈTßH_kJn6%*Â‡{P¶ÃS%qéT­{€SI½Ø:�8slOzìûÝ†eêi¥�HáÞc·ë7¤Ý/�Få9MÏn¬ÄÞù Ö�|0¸LAf«uàþ£uÀÔ˜0�ã8ÀÄ«V5¥–l…®ªdS¨ÀíÐbAÙ o¸âšId€wJ½äÌ_AÚS>¼“Á‹ZòßÔ¢ñt«^S8	è‰b·¨Ž€OåxÚAÌá»ÔÃ¹Ð>ÖÛ‹.xVÑÑg,ÒìãCª&�‘÷ñÓ±}`õ‡;=wÿ}]òº	xš eÿ#!/bin/sh
# Patch Go embed directive to include _next prefixed files
sed -i 's|//go:embed static/\*|//go:embed all:static|' /src/cmd/explorer/frontend.go
Ù55‹µx êÿ# rebuild 1776010884
B¹_¤x$ Ûÿ40000 workflows nÊ9w�Õ~ÿÐ4YQ_ïí‡3ºa÷üŽ¤x$ Ûÿ40000 workflows É¿ þAé„ân¸„“ct6‘èúkçõ:x èÿÇ½�rubuntu-latest‘‰¾n‘	Íì€ƒWx, ÓÿÞÕ�˜lux±�:lux“Ü'lux“(	lux-build“<"vÝo¤x$ Ûÿ40000 workflows  9[ñÚz¿ÝÇ—­>âLB’§Çðtrë€„Dx; ÄÿÞ¦�2branches: [main, dev, test]‘-D“„secrets: inherit
nm‘¤x$ Ûÿ40000 workflows 2Aµ‡îŠ¥(ZLM`æ*³¼Ô²¥x340031QH*ÍÌIÑ«ÌÍap|—Ä*­cÎï—�o£}útï‚ö> Ìî/xN ±ÿ%n�%I100644 ci.yml äìtî&úí_‘ÏÂXU�œ‚100644 release.yml 0Õ*Y˜Ùº$ 4ƒ…øAzb'—^GC±‡xíUMoÛ0½çWC�CíöqÈ íæ¥Á¶¶H»¡ÃVŠÍ$ZlÉ�ä4AÚÿ>RJœ:mÑw˜/‰IŠ|�z¤•(°Ç•Ì3He®šVK«NàF›Ù8×7ÃLÚR¸tÊÆ²²þ`d„J§h;ð«R]“À‰	öç¯÷Ù`°ÔV:m–�$·,ýI©2\ ‰ª2³˜QN·¯0m×­ª9ã$½þÅåàg&ÓÔÄR“­ÿí¨—Ô†v^-Æ²½Î@¬þè‘å£#fË ¬3Tv²o …pF.6o R¥y•áÖ �©”BC,-æãhª­žKU-@Ùû·¡\€Ÿ2n¬MÑÓö!¾|ð?—ÑÏfäŸ‘°Ùˆ®öV«5›8à…»;Q¢)¤µR+ß†�jåP9º8ƒ"[#+E:¾¤#z+1-ëST–Ý"uœ¬MZHgºr‡ó …["2�ÎÐ´s=‘*
á‡ó7ë:¤3éÖ’
ý08‘t1Ë@‚î;Þ\ö†Cˆ£äFyý2Ù	e©F1¥×¦W
kIËYÈg15èlÜë_ž|?^ž}IN·ñ;�t‚„)jÔïjÔ’ò±»6ìÒ�Ehƒc^�ÛJÌ!LËm�ÂÛh2ºÇ8§;é†){a±˜£9 ¹tÔ…î|µ¢W¾Õõ5‡…AëÚéæ??
Ïô#w	y™,\â{pü> gª �P6ðbâ^11÷)&i”5ž�Í6äb„9)é‰ÁÝ<€ÃÛ¦Ù¿Éà¢vÚ½§	êæ�…ÒLAûâôSr•†ƒäs—WØ=Z ½ÁÑùÉã®äêüëÙ Ù:é`!”£uaU(ÄŒ�y”>k=—Ï-�GÆóÅ“øÎþ¡9›é#mT‡PT¹“‘0é´J-6’FSÃô­àÍ@Ÿ§&b=B ãÏÞö	¨K m:®þj�¼‚ß�XZÏÍ„Ê^è¾½Ý�r ò¼Ø·õxlÒ=ŒÏ´ÂÖ_¶o¤í„xxM ²ÿñÍ°<lux-build-amd64“©“ulux-build-arm64³©Èlux-build-amd64“í³¤Í+Û�¤x$ Ûÿ40000 workflows ¬áwr…öÿ6ã'ÂÐ`/2„q <0ä„–Dxœ{Êò”EÉÄ ôÒ3K2J“"z_v]\vâUÚüßu››ôâ7*Ù2Š0øqÕ²Iì*qZÊ±&íðêoö“‹? ‰¥A¤x$ Ûÿ40000 workflows ÙÈ[ÎLdçC2BÅ_¶½üœX÷‡N¥x% Úÿ100644 build.yml táÍ‘¤Ó° ‰Ñï‹˜.W[¼álÓc‚à€5xœ{Êò”eC<£û©[#[Kµ®Íjõ�HHŸþmÉS½ÉÅŒŸ õx˜ì�ÜxœU�±JAEK³³	(6
aM‚K"‚i-l— 0»;›Ù¼æM„T¦¤qÿÀZ°²ÿÃÆ�°v6+QÛË»çÞûî�?Ÿ_×:çÜ Ësˆs™Œ’!H ù@�áš§ Ì�ƒÊ™É¤ÛÓJ`ç›Y½Áí~1#×ÚzµKûèí4PþC	´ï^ÓŠÚ›mùÙÝºçu»ió%·�ÇTI�>g 2§”)‡TrÂ¦£Y2*õ%6,1`eÖÜL´dXÔ‘ºW˜OWSJ£OÎÄŠcRÓŽ]ø{6©nŸtê¹ÌŸ(·;žþö�FB)�ƒb±f˜†€lÌÛöÉŸÛxca©¾eªÑÑŸÇ­€ÚàÒr>Ê'ƒ°lq=UŽ©UrÓ¿xÙm|U¾v½‡·àƒxœ{Êò”eC<£ˆîÓ½­&{<zWìó­ŸÄÞöÆýQ¥ääbÆO ñ†ï�Þixœ›dòCeÃNv›ÀÒÔ¢J…¤œüäìäŒÄÌ¼b…”Ê¼ÄÜÌäÄœœJ…GSòRËR‹2‹R’óSRÀªr2‹KŠ'[r˜NžÇ«¦¤¯¯4¥ '±$-¿(WÁ1À“‹“bžŽBjQ‘ÂäR^[ÆÚÉx-`ÂÅÙ™™yé“™øB&OçÌËGqGfÞäCœê“3ùLÖà7š¼‹¿lr·†5Ð` un™EÅ%
)™ÅÉù@×¥¦@�´]¡$#³X!©(1/E!)59?7è¥Ô´ÄÒœ’É�Ü7k7ß¶` &ZÙà…7xœ  ßÿåå°_4Å�|W}³ÍUVôíès÷ó¢¹Þ“sòþofá|�áxœ•UMoÓ@• 	j—‚„„¦V‰šÄ!F9 $¨HmH…"ª6ö:¶âØî®�¦‘9sà€ùÜ�8Á�¿À�À•?Áì:I7©Â¡Šú:ïÍÌËÎë§§?7¿~¿qçuHÙ1´Oïè±]k7×¯´ôøãÒ³uVb´mó€²F@\ƒ0£"kr-ÂiZÁ8³¼»¾@]>n,ÿŽs+Oâ+½HöÚ;ö)hePh¯«d3Û0ÛmóRÅs©Ðë¾ª@!X‘‡S,ÊœS‘i�¶`€:œÂ%¾CÓcC•Õ”Œ/SâÜí‡¸¶¢Ñ.éR \ü”À›zE‹ÿÞÒÎ§—pÃ™Ž•J×p¹" A>›dÙ¬ªBÇõŽÜÄèŸƒAh×sS8µž8R8xIðkD·F‹ Ý6#“2êI%‡?ï?ƒë@+z*j]m–²=Â¦º—EÿýÄ ƒý‰¢M¦ÝFG'ì+štø´�à:zbJ#”í°•"Ò"‰Šýµä»+@•š$tÒAaBÅŸ¡R/V†àPdX4EìÏ 6%1š`Š hâG9ñ¼Ôˆ“	‡*ï<jowŸ_(´?©yP­5§%Æ38öah©1pz’m‰ýß0‰N4=ÐPhæHé	L‹^ ¾ÜªÍÙÅ"îIÚQÄ¢“”©[¢n¡M¤¬OO©
hZ³ŽÈÉöÍäÔf‡ïLÂèb0€ŒŽêÈ¢îD Â¡LD“ØN\¼~u¶h\_‚{^G¤×Ä‰íË£>ˆ?dà±ë]ì¹ÁÀ;¶ïãbÇi½£Œ‚ôWæQÄ¥+z)ÉM2TÃývˆ"�Dh`�OÿÂ�ì 3B1oÇ¸Ex#4M»ŸC=j H‹ópv	åòøÎ5äGØZ(—A$7l‚¢Ò~ ¶tµ¢2_Wi!Þf^“I=G«:W+J‹‰÷z¹ØÞ”ØÄZþùZsÉõ1’ÌþÌ¦`Ãç}YáØ¸AòŽ2&^+½0Œœž.�µ2¸¶§ñ—kw×ÄÁ§¢ÁFßÆÈ,Ë'*ƒoñjtõ0¼c�àŒ xœ  ßÿåå°_B+äžŠ&©Ûp<çüä“�§ÿ“sòöÓá …ixœ}Q=JÄ@F¬2¢¢��±Ü,ÓRÈXKp-"2;3‰�ü1I$K9D."xàk¯áŒd7+f-¿÷½ïçñÞfï—/«ƒþóèâœ�øÀŸX”¨,i&dz,s%ƒ¨ÇŒQ¿‡ÎºaâÂR±TÀð#«EÈ*J=`Jfýþ�Gš¥A6È²šE\…r-ã?‘Ø†–Hg´:§sëQM°Bš9‘uI–'”¨œkön•k–Ï‰‹¬RÜÀD¤…Æ×2`U\®}]}@\Õ¸µ§ªä»ªxªt[]8ñÆ�ßM¦ë]�þ·sâã«M `ádZ‹¬ Sðh7�ÐÖ¡„ñ7Íë×ÉÇþ7C2›KàŽkxœ  ßÿåå°_ìÿ³õR¸Ë;õ‘’ÈÌ,ÕV]_“sò›oì�Kxœ]�ÍJÃ@…‰‰m3þ@u!XÁÛHµ…šºpQ‚.Ü(­J—
3Óšš&a2ÑJ)‚ø ê|—în|ŸÂµÏ •
î—s¿sÎÛÒÇÂmŒÊØbþØ‹ûÔ<Nãç®ƒëc÷&u#¬MèQ?2›ì¢lüùŒ*¬zg\êµ*Áu¯Ô²Z!ŸÕõ‚Û†b7p}Fa@t]çLÄÜ—jHðS…å”¶€Rd•¨d„Œqs‹Rþ+÷.*›š‘­}˜bo´2*Úl½m»žÄŠ "asaAé\çÒ8Ê�²6ã�î0["Ëx8~¤¥¹ø2¾‡µL=‰3sÊ´P&kü”1ð){µlÛžwb;g~™ùÔæœSÛõ#|ÈîæºÉknGI,(3#Ç÷°”‰ffß'
D~s×ïT`€�É•¼á|Cdå¦Ýcø8Ù(
³aU´¼¸#¯áWqª‘7úÿ®ÉÝÔ†ö¥©Ž<á’xœ{ÊÒÏ4As¢ÿÜÉeŒê“�™” ;ëÜéx) Öÿ�µ�).gitignore ‘íZ‹cVóæÝiZ7Á±Õ2º¸‘"í^Ö„§	x— hÿ40000 .github é*MôÙ
š(‘ªQ7È×«a100644 Dockerfile ø^&V¬-ö¿…Ïc’ ç÷Þ100644 README.md ÎÔKVÑ¤æKŒÜÑ9¦
ã*å100755 patch-embed.sh 6&Í«(Y‚_u?wJü*¯õV¬xâ9¤x$ Ûÿ40000 workflows b˜2oQ`Ÿ�fwX‰Q¬d(j×‹VäzØ¥x% Úÿ100644 build.yml /x%üÈãïÉ=ÌA,�©:óñí·ôâ›rxœû(°‚sÂÄ‰´µ¹²ò“Š­¸Rò“³S‹@,…¢Ò¼bÝü<+…Ò¤Ò¼’RÝœÄ’Ôâ’ÉoëÅŠSKJt“J3sR*t“K2óóÊŒ'²‰MÎaú/äêî©P[«ŸSZ‘–©ŸZQ�“_”Z4ù6[ýä*6ÞÉÿ™wZ%'&g¤ê¦åçZ)”T¤Ú¦g$‚m† ˆtI>BR'7?%Õ67±bòZ–ÜÉ¦¬‚ ~^Dcä)€–xœ]RMkA&äÔõP¡XŠ¾˜ IÈÒ öšÖµÔnj>ÄBšÙÝ·Ùm73ÛÙYI’ÐCÁ[ur�Á«g½	žÜ_ xôâì&Æ¥Ã<Ãð~<ïÇŸ�ÏË“||~ñö¬•Ò×§	ÉÛ¹JÊ{¹I¾QùeéV>žLå¯¥�wžD¡ á"xÔÁr(Ù.ñ(8Dh¶w€Qÿ´,^½)¿iGÕ…ù€“À¤�"”ÂÈš}l'wÇL½ä‹+¯ÇMn»ž@[D5­ßïk8
|Æ“Luð†d€Uh€åQÂ=ËZ<}O'ê,Ô^ÕkÿÜjH°õ_e×è=K¤f\gbV –¾O|X@~‚µqÆx9ÉS�Ë0†::°‡#qï(„CÎ¨@ê¨ g™ ½Ž©Ê„ ¹>k`È™n3zèª°ß4a¸ÊÌx�àÑ&ì>—?r×Æ�‰ê±KB|ÆŽ£ T†Ó¹aß³‰ÊŸ¿ƒ^ã²ÚuÚ5›ŸB©.µL}×hél”Óîk…ì'ÑMb¡ŸŽÄR¹4‡ÙÇªÙ<’íëwåòŠ!Wo¬É…MP«ÛfeÍ‡R…úýõŠPþ.4Ò}*_¦ü¦ØNù{þê|ëA­xm ’ÿ40000 .github é*MôÙ
š(‘ªQ7È×«a100644 Dockerfile „éÙÌÊ^[Žšƒv¸�N;žúª100644 README.md ;¡1ÄàN{8B·S^g¤ gS³z(ïå%„œxœUQÑjA%&ié*‚’¶n’j2)´¡]¡MÛÄlØe³3i¦�ÌlggÃö½úâKË¼
~ƒøþ@ÿ¡úìˆèlcRú23gî™sî=sžûòèbå%PŽIL$ô=ÿ„pk@â€	I` WÉå°tä«háp(	™àð¨$I Bû²™Ï³(P4UXm‰™¶àì¬¤¦–*¦ðóãLÈSCCî�XUŽÃ[ïé“|ý×(¯ª!mðÅ¨O9Á¥›Þv#Ê°€…q4C%ÊêòÇ“ž„Š!¸7åAFÊlU·«v²@™€Ûm¾ßïìu�vç°1T*°â{»¶Y5(Ô÷QÉÀ7²)©?¥Î¾µï}Héßi\+Â�ÇX2°t�d)¤‚�G™ÉÊ—ÄSF”›ô„ÊSÔL¥~—ÙÒŸ3�1Å�‚Bž×‘Áú:³;7	P—²kÓã›lA§Ó9ý5»üàµã¾Øk»€Béë?ÙñS³#„gi¡‰•µã¶àÕ¾Ûk;�&cýw®fµ§×`”G±Þ˜¯çßZI<e·QWîhéÅ…Óùÿª¯
ºkëïV¤ÝÏýûkÆ3ä$‚PxD»ýŸò
�:‘#
#
# Uses the pre-built indexer image as base, adds the frontend static export.
# No Go compilation needed â€” just layers the frontend on top.
#‘j!*Go backend (built by luxfi/indexer CI)
#  “Q7   â†� Next.js frontend (built by luxfi/explore CI)
#  “Q“"This: combined

ARG INDEXER_IMAGE=‘o:main
ARG NODE_VERSION=22“Elstatic export³°ƒ“j7\
   “ G“ü6@Combine indexer + frontend ----
FROM ${INDEXER_IMAGE}

USER root“*"v/frontend
USER explorer

# The explorer binary serves /srv/frontend at /* and API at /v1/explorer/*
ENV FRONTEND_DIR=/srv/frontend
_ä½Ê¶hx†yù# Explorer

Combined block explorer image: Go backend ([`luxfi/indexer`](https://github.com/luxfi/indexer)) + Next.js frontend ([`luxfi/explore`](https://github.com/luxfi/explore)) in a single container.

```
ghcr.io/luxfi/indexer   â†� Go backend (API + chain indexing)
ghcr.io/luxfi/explore   â†� Next.js frontend
ghcr.io/luxfi/explorer  â†� This: combined single image
```

## Run

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=http://node:9650/ext/bc/C/rpc \
  -e CHAIN_NAME="My Chain" \
  -e COIN_SYMBOL=ETH \
  -v explorer-data:/data \
  ghcr.io/luxfi/explorer
```

Single port, single image. Backend API at `/v1/explorer/*`, frontend at `/`.

## Build

```bash
docker build -t ghcr.io/luxfi/explorer .
```

Pulls `luxfi/indexer` and `luxfi/explore` from GitHub at build time. Override branches:

```bash
docker build \
  --build-arg INDEXER_REF=v1.2.0 \
  --build-arg EXPLORE_REF=v2.7.2 \
  -t ghcr.io/luxfi/explorer .
```

## White-Label

```bash
docker run -p 8090:8090 \
  -e RPC_ENDPOINT=http://node:8545 \
  -e CHAIN_NAME="Awesome Chain" \
  -e COIN_SYMBOL=AWE \
  -e CHAIN_ID=12345 \
  ghcr.io/luxfi/explorer
```

## Multi-Chain

Mount a `chains.yaml`:

```bash
docker run -p 8090:8090 \
  -v ./chains.yaml:/etc/explorer/chains.yaml \
  -v explorer-data:/data \
  ghcr.io/luxfi/explorer --config=/etc/explorer/chains.yaml
```

## Related

- [`luxfi/indexer`](https://github.com/luxfi/indexer) â€” Go backend, 1528 tests, SQLite, E2E PQ encrypted backups
- [`luxfi/explore`](https://github.com/luxfi/explore) â€” Next.js frontend, Geist font, white-label
- [`luxfi/explorer-v1`](https://github.com/luxfi/explorer-v1) â€” Legacy Elixir stack (archived)
(C­xm ’ÿ40000 .github é*MôÙ
š(‘ªQ7È×«a100644 Dockerfile *n†Ÿ¸Št)g˜Ú*zïa]wL100644 README.md ;¡1ÄàN{8B·S^g¤ gS@&«ûMÌß±�p PÁh ØŽbW³%)