Httptap - ดูคำขอ HTTP/HTTPS ของโปรแกรม Linux ใดๆ ได้
(github.com/monasticacademy)- httptap เป็นเครื่องมือที่รันโปรแกรม Linux ในรูปแบบ
httptap -- <command>แล้วแสดงสรุปคำขอและการตอบกลับ HTTP/HTTPS ที่โปรแกรมนั้นสร้างขึ้นในเทอร์มินัล - ทำงานได้โดยไม่ต้องใช้สิทธิ์ root, daemon, การเปลี่ยนแปลงทั้งระบบ, กฎ
iptablesหรือการเปลี่ยน routing table และไฟล์รันเป็น ไบนารี Go แบบ static ที่ไม่มี dependency - ปัจจุบันรองรับเฉพาะ Linux และระบุว่ายากต่อการพอร์ตไปยังระบบปฏิบัติการอื่น เพราะใช้ system call เฉพาะ Linux เช่น network namespace
- ทราฟฟิก HTTPS ถูกถอดรหัสโดยสร้าง Certificate Authority ขึ้นทันทีตอนรันแล้วฉีดเข้าไปในสภาพแวดล้อมของ subprocess และทำงานแบบ transparent TCP proxy ที่จัดการแพ็กเก็ต IP/TCP/UDP ดิบ
- บน Ubuntu 23.10 ขึ้นไป หรือดิสโทรที่ปิดใช้งาน unprivileged user namespace เป็นค่าเริ่มต้น อาจต้องตั้งค่า
sysctlและมีข้อจำกัดอย่างการรับ connection ขาเข้าและการเข้าถึง/dev/net/tun
สิ่งที่ httptap ทำ
- httptap ครอบคำสั่ง Linux ใดๆ เพื่อรัน พร้อมแสดงคำขอ HTTP/HTTPS ที่คำสั่งนั้นสร้างขึ้น
- รูปแบบการใช้งานพื้นฐานคือ
httptap -- <command> - ตัวอย่างเมื่อรัน
curl https://monasticacademy.orgจะแสดงคำขอGET https://monasticacademy.org/และการตอบกลับ308
- รูปแบบการใช้งานพื้นฐานคือ
- ในตัวอย่าง Python
requests.get('https://monasticacademy.org')จะแสดงสองคำขอขณะตาม redirect- คำขอแรกได้
308สำหรับhttps://monasticacademy.org/ - คำขอถัดไปได้
200สำหรับhttps://www.monasticacademy.org/
- คำขอแรกได้
การติดตั้งและเงื่อนไขการรัน
- ไบนารีที่ build ไว้ล่วงหน้าสามารถติดตั้งได้โดยดาวน์โหลด tarball ของ release ล่าสุด
- ดูทุกเวอร์ชันและสถาปัตยกรรม CPU ได้ที่ releases
- มีวิธีติดตั้งผ่าน Go ด้วย
go install github.com/monasticacademy/httptap@latest
- โดยทั่วไปการรันไม่ต้องใช้สิทธิ์ root และไม่ต้องมี daemon หรือการตั้งค่าทั้งระบบ
- ไม่สร้างกฎ
iptables - ไม่เปลี่ยน routing table
- โดยทั่วไปไม่กระทบโปรเซสอื่นบนระบบเดียวกัน
- ไม่สร้างกฎ
- บน Ubuntu 23.10 ขึ้นไป ต้องตั้งค่าดังนี้
sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- การตั้งค่านี้จะปิดฟีเจอร์ของ kernel รุ่นใหม่ที่จำกัด user namespace แบบไม่ใช้สิทธิ์พิเศษ
- อาจจำเป็นบนดิสโทรอื่นที่ปิดใช้งาน unprivileged user namespace เป็นค่าเริ่มต้นด้วย
- กำลังสำรวจวิธีแนบโปรไฟล์ AppArmor ให้ httptap เพื่อตัดความจำเป็นนี้ออกไป
ตัวอย่างการใช้งาน
- ตัวอย่างการรัน
curl -s https://buddhismforai.sutra.co -o /dev/nullแสดงว่าเซิร์ฟเวอร์ส่ง redirect302กลับมา - หากให้ตาม redirect เช่น
curl -sLจะแสดงคำขอเพิ่มเติมด้วย- คำขอแรกคือ
302 - คำขอที่สองคือ
200สำหรับ URL ปลายทางของ redirect
- คำขอแรกคือ
- ในตัวอย่าง
gcloud compute instances listสามารถดู HTTP endpoint ที่ Google Cloud CLI ใช้งานภายในได้POST https://oauth2.googleapis.com/tokenGET https://compute.googleapis.com/compute/v1/.../aggregated/instances?...- เอาต์พุตปกติของ
gcloudจะแสดงร่วมกับ log คำขอของ httptap
- ในตัวอย่าง
kubectl get allจะแสดงคำขอไปยัง Kubernetes API server--https 443 6443ทำให้ถือว่า TCP connection ที่พอร์ต 443 และ 6443 เป็น HTTPS- ต้องใช้
--insecure-skip-tls-verifyเพราะ kubectl ไม่ได้ใช้ Certificate Authority ที่ httptap สร้างขึ้น
- ตัวอย่าง
curl --doh-url https://cloudflare-dns.com/dns-queryแสดง flow ของ DNS-over-HTTP- สองคำขอแรกเป็นการ lookup DNS
- สองคำขอถัดไปเป็นคำขอ HTTP ปกติไปยังเว็บไซต์ปลายทาง
- หากใช้ตัวเลือก
--headและ--bodyร่วมกัน จะแสดง HTTP header และ payload ดิบ
เอาต์พุต HAR
- ใช้ตัวเลือก
--dump-har out.harเพื่อ dump คำขอและการตอบกลับ HTTP เป็น ไฟล์ HAR ได้ - ไฟล์ HAR ที่สร้างขึ้นสามารถนำไปแสดงผลใน HAR viewer หลายตัวได้
- ยกตัวอย่าง Google HAR Analyzer
- ตัวอย่าง HAR มีคำขอ
308 Redirectไปยังhttps://monasticacademy.orgและคำขอ200 OKไปยังhttps://www.monasticacademy.org
การเข้าถึง localhost
- หากต้องการเข้าถึงพอร์ต localhost ของ host จากภายใน httptap ให้ใช้
host.httptap.localหรือ169.254.77.65แทนlocalhost - network namespace แต่ละตัวของ Linux มีอุปกรณ์ loopback
127.0.0.1ของตัวเอง ดังนั้น127.0.0.1:1234ภายใน httptap จึงไม่ใช่ address และพอร์ตเดียวกันบน host - httptap hardcode ให้ route
169.254.77.65ไปยัง127.0.0.1เพื่อเลี่ยงปัญหานี้
Subprocess ที่กลายเป็น daemon
- บน Linux สามารถสร้าง subprocess ที่ยังคงอยู่หลังจากโปรเซสดั้งเดิมจบไปแล้วได้
- เป็นวิธีทั่วไปสำหรับ daemon และแอป GUI ที่รันจาก command line
- หากรันโปรเซสลักษณะนี้ภายใต้ httptap โปรเซสที่ daemonize แล้วจะยังคงอยู่ใน network namespace ของ httptap ต่อไป
- ตัวเลือก
--no-exitทำให้ httptap ยังคงทำ proxy และ logging ต่อหลังจาก subprocess ที่รันทันทีจบลงแล้ว- ในตัวอย่าง Visual Studio Code ใช้รูปแบบ
httptap --no-exit -- code --ignore-certificate-errors . - หากต้องการจบ ให้ปิด VS Code แล้วกด
Ctrl+Cเพื่อปิด httptap
- ในตัวอย่าง Visual Studio Code ใช้รูปแบบ
- หาก httptap จบก่อนโดยไม่มี
--no-exitแม้ network namespace จะยังอยู่ แต่จะไม่มีโปรเซสที่อ่านแพ็กเก็ตจากอุปกรณ์ TUN ทำให้การเชื่อมต่อเครือข่ายของแอปถูกตัด - ในตัวอย่าง
setsid setsid curl http://httpbin.org/getและตัวอย่าง Python ที่ใช้fork,setsid,forkหากไม่มี--no-exitจะเกิดข้อผิดพลาดCould not resolve host
วิธีทำงาน
httptap -- <command>รัน<command>ใน network namespace ที่แยกออกมา และฉีด Certificate Authority ที่สร้างขึ้นทันทีเพื่อถอดรหัสทราฟฟิก HTTPS- สร้างอุปกรณ์ TUN ของ Linux แล้วตั้งค่าสภาพแวดล้อมของ subprocess ให้ทราฟฟิกเครือข่ายทั้งหมดผ่านอุปกรณ์นั้น
- ทราฟฟิกที่เขียนไปยังอุปกรณ์ TUN จะถูกส่งต่อไปยัง file descriptor ของโปรเซสที่สร้างอุปกรณ์นั้น
- หากเปลี่ยน root network namespace จะกระทบทราฟฟิกของทั้งระบบ ดังนั้น httptap จึงสร้าง network namespace แยกต่างหาก
- namespace นั้นมีเพียงอุปกรณ์ loopback และอุปกรณ์ TUN
- subprocess จะรันอยู่ภายใน namespace นี้
- ทราฟฟิกที่ได้รับจากอุปกรณ์ TUN เป็นแพ็กเก็ต IP ดิบ
- httptap parse แพ็กเก็ต IP และแพ็กเก็ต TCP/UDP ภายใน
- และต้องเขียนแพ็กเก็ต IP ดิบกลับไปยัง subprocess
- implementation TCP/IP ของตัวเองยังขาดหลายส่วนของโปรโตคอล TCP ทั้งหมด แต่ทำงานได้สมเหตุสมผลสำหรับจุดประสงค์นี้
- เมื่อ subprocess ส่งคำขอไปยัง
www.example.comhttptap จะรับ TCP SYN ที่มุ่งไปยัง IP ปลายทาง แล้วตอบกลับด้วย SYN+ACK- จากนั้นจะใช้ socket API ปกติของ Linux แยกต่างหากเพื่อเปิด TCP connection ไปยัง IP ปลายทางจริง
- หลังจากนั้นจึง relay ข้อมูลสองทิศทาง
- โครงสร้างนี้คือ transparent TCP proxy แบบดั้งเดิม
- การถอดรหัส HTTPS ทำผ่านการฉีด Certificate Authority
- ตอนเริ่มจะสร้าง Certificate Authority ที่ประกอบด้วย private key และ x509 certificate
- เขียน certificate ลงใน filesystem ที่มองเห็นเฉพาะ subprocess
- ตั้ง environment variable ที่มองเห็นเฉพาะ subprocess เพื่อเพิ่ม Certificate Authority นั้นเข้า trust list
- เนื่องจาก httptap ถือ private key ของ Certificate Authority อยู่ จึงสามารถพิสูจน์ตัวเองเหมือนเป็นเซิร์ฟเวอร์ที่ subprocess ต้องการสื่อสารด้วย และอ่านคำขอ HTTP แบบ plaintext ได้
ข้อจำกัด
- ปัจจุบันรองรับเฉพาะ Linux และพึ่งพา system call เฉพาะ Linux เช่น network namespace
- โปรเซสไม่สามารถรับ connection เครือข่ายขาเข้าได้
- ต้องมีสิทธิ์เข้าถึง
/dev/net/tun - คำขอ ICMP echo ทั้งหมดจะถูก echo กลับโดยตรง โดยไม่ส่งแพ็กเก็ต ICMP ออกไปยังเครือข่ายจริง
2 ความคิดเห็น
it was developed at the Monastic Academy in Vermont in the US. We believe that a monastic schedule, and the practice of the Buddhist spiritual path more generally, provide ideal conditions for technological development.
ความคิดเห็นบน Hacker News
ส่วน “How it was made” ใน README น่าสนใจพอ ๆ กับตัวเครื่องมือเอง
มีเนื้อหาว่าพวกเขาอาศัยและปฏิบัติธรรมร่วมกันบนที่ดินขนาดมากกว่า 100 เอเคอร์เล็กน้อย สวดมนต์และนั่งสมาธิร่วมกันเช้าเย็น และในแต่ละเดือนจะจัดและเข้าร่วมรีทรีตสมาธิประมาณ 1 สัปดาห์ เวลาที่เหลือก็ช่วยกันดูแลที่ดิน บำรุงรักษาอาคาร ทำอาหาร ทำความสะอาด วางแผน ระดมทุน และในช่วงไม่กี่ปีมานี้ยังรวมถึงการพัฒนาซอฟต์แวร์ด้วย
ในยุคที่ไมโครโค้ดและลอจิกก่อปัญหาในระดับนาโนวินาที มีเรื่องเล่าว่าวิศวกรที่ทำงานหนักเกินไปคนหนึ่งลาออก แล้วทิ้งโน้ตลาออกไว้บนเทอร์มินัลว่า “ผมจะไปอยู่คอมมูนใน Vermont และจะไม่ยุ่งกับหน่วยเวลาที่สั้นกว่าฤดูกาลอีก”
ส่วนจะเป็นไอเดียที่ดีไหม หรือจะนำไปสู่ผลลัพธ์อย่างที่พวกเขาจินตนาการหรือไม่ นั่นเป็นอีกเรื่องหนึ่ง
การผสมผสานระหว่างเทคโนโลยีกับการทำสมาธินั้นน่าดึงดูด ตัวไอเดียน่าสนใจ แต่การทำจริงคงยาก น่าจะคล้าย ๆ Recurse แบบพุทธ
httptap เป็นตัวติดตาม HTTP ในขอบเขตของโปรเซสที่รันได้โดยไม่ต้องใช้สิทธิ์ root
เมื่อรันโปรแกรมลินุกซ์แบบ
httptap <โปรแกรม>ก็จะเห็น trace ของคำขอและคำตอบ HTTP/HTTPS ทาง stdouthttptap -- python -c "import requests; requests.get('https://monasticacademy.org')"---> GET https://monasticacademy.org/<--- 308 https://monasticacademy.org/ (15 bytes)---> GET https://www.monasticacademy.org/<--- 200 https://www.monasticacademy.org/ (5796 bytes)วิธีการคือรันภายใน network namespace ที่แยกออกมา และใช้ gVisor เป็น TCP/IP stack ของตัวเอง ไม่ใช่ HTTP proxy จึงไม่พึ่งพาการตั้งค่า proxy ส่วนทราฟฟิก TLS จะถูกถอดรหัสด้วยการสร้าง CA ขึ้นมาทันที และไม่ติดตั้งกฎ
iptablesหรือทำการเปลี่ยนแปลงระบบแบบ globalnetstackสำหรับฟีเจอร์บางอย่างบน macOSโดยเฉพาะถ้าไม่ต้องตั้งค่า proxy ก็จะยิ่งมีประโยชน์
https://blog.wireshark.org/2010/02/running-wireshark-as-you/
ไอเดียการรันโปรเซสใน network namespace ที่แยกออกมานั้นอัจฉริยะมาก
ส่วน HTTPS น่าสนใจกว่า ดูเหมือนว่าจะตั้ง environment variable ทั่วไป[1] ให้โปรแกรมใช้ CA bundle ในไดเรกทอรีชั่วคราว แต่ปัญหาคือโปรแกรมอาจไม่สนใจตัวแปรนั้นเลย เหมือนกับตัวแปรกลุ่ม
http_proxyยังเห็นด้วยว่ามีการเมานต์ overlay filesystem ไว้ที่
/etc/resolv.conf[2] สงสัยว่าจะช่วยไหมถ้า httptap เมานต์ไดเรกทอรี/etc/ca-certificatesเป็น CA bundle ชั่วคราว[1] https://github.com/monasticacademy/httptap/blob/cb92ee3acfb2...
[2] https://github.com/monasticacademy/httptap/blob/cb92ee3acfb2...
ที่ httptap เมานต์ overlay บน
/etc/resolv.confก็เพราะการแก้ DNS มีปัญหาคล้ายกับ CA root คือไม่มีวิธีที่แน่นอนในการบอกโปรเซสใด ๆ ให้ใช้ DNS server ตัวใด อย่างไรก็ตาม/etc/resolv.confก็เป็นตัวเลือกที่ค่อนข้างเชื่อถือได้ ทันทีที่ใส่โปรเซสเข้าไปใน network namespace ก็จะเข้าถึง systemd resolverlocalhost:53ซึ่งเป็นการตั้งค่าที่พบบ่อยที่สุดบนเดสก์ท็อปลินุกซ์ไม่ได้อีกต่อไป จึงต้องจัดเตรียมการแก้ DNS ให้การเมานต์
/etc/ca-certificatesแบบ overlay ก็อาจช่วยได้เช่นกัน แต่พอลองดูโครงสร้างไดเรกทอรีนั้นแล้วก็แปลกใจว่ามันไม่สอดคล้องกันมากระหว่างแต่ละดิสโทร ถึงอย่างนั้นก็ทำได้ ถ้าใครรู้วิธีเพิ่มใบรับรองเข้าไปในไดเรกทอรีนั้นในแบบที่อย่างน้อย TLS implementation บางตัวจะรู้จัก ก็อยากฟังตัวอย่างโต้แย้งคือ ลองจินตนาการถึงมัลแวร์ที่สื่อสารผ่าน TLS และ obfuscate โค้ดที่คอมไพล์แล้วอย่างหนัก อาจเป็นโปรแกรมที่ฝังชุดใบรับรอง CA แบบตายตัวไว้ในไบนารี และไม่เปิดไฟล์ระบบเลย แต่ถึงอย่างนั้นก็ยังสร้างการเชื่อมต่อ TLS ที่ปลอดภัยได้ราว 10 ปีจนกว่าใบรับรอง root CA ส่วนใหญ่จะหมดอายุ TLS ทั้งหมดประมวลผลใน user space และไม่มีการรับประกันว่าจะใช้ OpenSSL หรือไลบรารีร่วมอื่น ๆ ดังนั้นจึงคาดหวังไม่ได้ว่าจะ hook ฟังก์ชัน OpenSSL เฉพาะเจาะจงได้ ถ้าเซิร์ฟเวอร์ใช้ใบรับรอง self-signed แล้วไคลเอนต์ยอมรับไม่ว่าด้วยเหตุผลใดก็ตาม ก็ยิ่งแย่ลง
ถึงอย่างนั้น ด้วยการลงแรงเพิ่มเล็กน้อย ก็น่าจะจัดการเคส 99% ได้อย่างเสถียรแน่นอน ดีกว่าไม่มีอะไรเลย
การใช้ อุปกรณ์ TUN ตรงนี้เป็นไอเดียที่เจ๋งมาก ส่วน “How it was made” ใน README ก็จัดว่าเป็นหนึ่งในส่วนที่ดีที่สุดเท่าที่เคยอ่านใน GitHub README เลย
ผมกำลังทำสิ่งที่ชื่อ Subtrace[1] อยู่ ซึ่งสามารถดักจับทั้งคำขอขาเข้าและขาออกได้โดยอัตโนมัติ น่าขำที่ดูเหมือนว่าอินเทอร์เฟซสำหรับเริ่มโปรแกรมจะค่อย ๆ มาบรรจบกันในรูปแบบเดียวกัน[2] แต่เป้าหมายของ Subtrace ต่างจาก httptap เล็กน้อย และใกล้กับด้าน observability/monitoring ของบริการแบ็กเอนด์บนคลาวด์มากกว่า จึงเน้นคำขอแบบสองทิศทาง แนวทางก็แตกต่างกันด้วย โดยใช้ Seccomp BPF ดัก system call ประมาณ 10 ตัว เช่น
socket,connect,listen,acceptแล้วพร็อกซีการเชื่อมต่อ TCP ทั้งหมดผ่าน Subtrace จากนั้นจึงพาร์สคำขอ HTTP จาก TCP stream และนำแท็บ Network ของ Chrome DevTools มาปรับใช้ใหม่ให้ทำงานในเบราว์เซอร์เหมือนเว็บแอปทั่วไปเพื่อแสดงให้ผู้ใช้เห็นอยากรู้ว่ามีเรื่องเล่าสนุก ๆ อะไรบ้างจากการรันโปรแกรมใต้ httptap และอยากรู้ด้วยว่าโปรแกรมไหน “โทรกลับบ้าน” บ่อยที่สุด
[1] https://github.com/subtrace/subtrace
[2] https://docs.subtrace.dev/quickstart
https://github.com/M66B/NetGuard
อาจฟังดูไม่น่าเชื่อ แต่ในไดเรกทอรี
networktabของรีโพมีความพยายามที่ทำไว้ครึ่ง ๆ กลาง ๆ เพื่อทำสิ่งเดียวกันกับแท็บ Network ของ Firefox อยู่ เป็นโปรเจกต์ที่เจ๋งมาก เลยอยากเรียนรู้เพิ่มเติม และยินดีคุยต่อเครื่องมืออีกแบบที่ผู้ใช้ที่ไม่มีสิทธิ์พิเศษสามารถใช้วิเคราะห์ทราฟฟิกเครือข่ายได้คือ rootless Podman กับ Pasta
แค่เพิ่มสิ่งต่อไปนี้ในตัวเลือก
podman run--network=pasta:--pcap,myfile.pcapแล้ว Pasta จะบันทึกทราฟฟิกเครือข่ายเป็นไฟล์ PCAP ซึ่งสามารถนำไปวิเคราะห์ภายหลังได้ ผมเขียนตัวอย่างง่าย ๆ สำหรับวิเคราะห์ไฟล์ PCAP ที่บันทึกไว้ด้วย
tsharkไว้ด้วยhttps://github.com/eriksjolund/podman-networking-docs?tab=re...
น่าสนใจทีเดียว ผมเคยเขียนไลบรารีที่ทำฟังก์ชัน “tap” คล้าย ๆ กันในแอปพลิเคชัน Go: https://github.com/henvic/httpretty
https://asciinema.org/a/297429
ผมก็เคยคิดว่าจะลองทำแบบนี้กับโปรแกรมใด ๆ ก็ได้เหมือนกัน แต่ไม่ได้ลงลึกจริงจังว่าจะ implement อย่างไร ดีใจที่เห็นมีคนทำออกมาแล้ว
สงสัยว่าทำไมไม่ใช้ eBPF ถ้าใช้ก็น่าจะดูคำขอ HTTP ของทุกโปรเซสได้พร้อมกัน รวมถึงโปรเซสที่กำลังรันอยู่แล้วด้วย แถมอาจไม่ต้องสนใจ TLS เช่น hook ที่
write(2)ก็ได้write(2)จะแก้ TLS ได้อย่างไร คุณอาจอ่านและแก้ไข ciphertext ได้ แต่โปรเซสไม่ได้เรียกwrite(2)ด้วยไบต์ plaintext ดังนั้นจึงอ่านคำขอ HTTP จริงไม่ได้ สุดท้ายก็จะเห็นแค่ไบต์ที่เข้ารหัสแล้วบนเครือข่าย ซึ่ง NSA ก็เห็นได้เหมือนกันต้องใช้ ทริกใบรับรอง CA แบบที่ httptap ใช้ แน่นอนว่ามีข้อควรระวังอย่าง certificate pinning แต่ในสถานการณ์ใช้งานจริงส่วนใหญ่สามารถทำให้ทำงานได้อย่างเสถียร
ตอนทำ Subtrace[1] ผมคิดเรื่องเฉพาะนี้มาเยอะจนแทบเกินเหตุ ดังนั้นถ้ามีแนวทางที่ง่ายกว่าหรือสวยงามกว่านี้ ผมสนใจจริง ๆ
[1] https://github.com/subtrace/subtrace
writeจึงไม่ช่วยเรื่อง การถอดรหัส TLSอย่างไรก็ดี มีตัวเลือกมากขึ้นก็ยิ่งดี
เจ๋ง น่าจะเอาไปใช้ ดีบักการตั้งค่า nginx ได้ทันที
ตอนนี้ใช้
curl -vแล้วต้องไล่อ่านเอาเองจากเอาต์พุตว่าผิดตรงไหน แต่เครื่องมือนี้น่าจะทำให้สิ่งอย่าง redirect loop โผล่ให้เห็นทันทีดูดีมากสำหรับเวลาที่ต้องสำรวจ call stack ของ HTTP/S ในแอปแบบเร็ว ๆ คร่าว ๆ
ส่วนตัวผมชอบ eBPF สำหรับการดูทุกอย่าง แต่ยูทิลิตีนี้อาจช่วยจำกัดขอบเขตส่วนสำคัญในการ trace ด้วย eBPF ได้
ดูดีนะ
โปรไฟล์ GitHub ชี้ไปที่ https://www.monasticacademy.org/about ซึ่งตัวมันเองผมไม่ได้มีความเห็นอะไรเป็นพิเศษ แต่สงสัยว่าการรีทรีตฝึกแบบอารามของพวกเขาเชื่อมโยงกับโปรเจกต์ GitHub นี้อย่างไร
พอดูท้าย README ก็เห็นว่ามีอธิบายความเชื่อมโยงไว้: https://github.com/monasticacademy/httptap?tab=readme-ov-fil...
ความหมายคือ ที่ดิน 123 เอเคอร์ในรัฐเวอร์มอนต์มีผู้คนอาศัยอยู่ร่วมกันตามโครงสร้างแบบอารามพุทธที่ค่อนข้างดั้งเดิม เพียงแต่ไม่ได้เป็นพระที่บวชอย่างเป็นทางการ ตอนกลางวันพวกเขาทำโปรเจกต์ต่าง ๆ ทั้งด้านเทคนิคและไม่ใช่เทคนิคร่วมกัน ลิงก์ README ด้านบนให้ภาพรวมได้ดี
https://github.com/monasticacademy/httptap?tab=readme-ov-fil...