Skip to content

Güvendeyim panosu ve duyurular / "I'm safe" board and announcements - #15

Open
Proxy34 wants to merge 8 commits into
schematrix:mainfrom
Proxy34:panolar
Open

Proxy34 wants to merge 8 commits into
schematrix:mainfrom
Proxy34:panolar

Conversation

@Proxy34

@Proxy34 Proxy34 commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Güvendeyim panosu (http://box.lan/home/guvendeyim.html) ve ana sayfada duyuru.

  • Ağa bağlı herkes adını, "Güvendeyim" ya da "Yardım lazım" durumunu, isteğe bağlı kaba bir konumu ve kısa bir mesajı bırakabilir, başkalarını isimle arayabilir. Arama Türkçe harflere takılmaz: ayse yazınca Ayşe, ismail yazınca İsmail bulunur.
  • Kayıtlar yalnızca kutuda durur, hiçbir yere gönderilmez, günlüğe yazılmaz. Kaydı yazan kişi 6 haneli bir silme kodu alır.
  • Kötüye kullanıma karşı: aynı cihazdan 10 dakikada en fazla 10 kayıt, yanlış silme kodu denemeleri sınırlı (kod tek tek denenemez), alan uzunlukları sınırlı, en fazla 20.000 kayıt. Sayfa tam adres, telefon ya da kimlik numarası yazılmamasını söyler ve kayıtların doğrulanmadığını, herkesin başkası adına yazabileceğini belirtir.
  • Kutu sahibi (savaş senaryosu için): sudo ./scripts/pano.sh kayitlar Ayşe kayıtları numarasıyla listeler, sil <numara> tek kaydı siler, temizle tüm kayıtları siler, kapat / ac panoyu kapatıp açar.
  • Duyuru: sudo ./scripts/pano.sh duyuru "Su dağıtımı 14:00'te okul bahçesinde" ana sayfanın en üstünde görünür; yalnızca kutu sahibi yazabilir.
  • Servis Python standart kütüphanesiyle yazıldı, yetkisiz bir kullanıcıyla (DynamicUser) çalışır.

Test: Debian 13 + Internet-in-a-Box sanal makinesinde: kayıt, Türkçe arama, eksik/uzun/HTML içerikli girişler, hız sınırları, kodla silme, sahip komutları, yeniden başlatma sonrası kalıcılık ve tarayıcıdan uçtan uca kullanım.


"Güvendeyim" ("I'm safe") board and a homepage announcement.

  • Anyone on the network can leave their name, "safe" or "need help", an optional rough location and a short message, and search others by name. Search folds Turkish letters (ayse finds Ayşe).
  • Entries stay on the box only and are never logged. The writer gets a 6-digit delete code.
  • Abuse limits: 10 entries per device per 10 minutes, rate-limited wrong delete codes, field length limits, at most 20,000 entries. The page tells users not to post full addresses, phone or ID numbers, and that entries are unverified and anyone can post on someone else's behalf.
  • Box owner: sudo ./scripts/pano.sh kayitlar Ayşe lists entries with ids, sil <id> deletes one, temizle wipes all entries, kapat / ac disables / enables the board; duyuru "..." puts an announcement at the top of the homepage.
  • Python stdlib service, runs unprivileged (DynamicUser).

Tested in a Debian 13 + IIAB VM, including rate limits, owner commands, reboots and end-to-end browser use.

Firewall (Proxy34)

…e duyurular

Ağa bağlı herkes güvende/yardım lazım kaydı bırakıp isimle arayabilir (Türkçe harflere duyarsız). Kayıtlar yalnızca kutuda; silme kodu, hız sınırları, kayıt üst sınırı. Kutu sahibi scripts/pano.sh ile duyuru yayınlar, tüm kayıtları siler ya da panoyu kapatır.
@schematrix

Copy link
Copy Markdown
Owner

Fikir de çok güzel, kötüye kullanıma karşı düşündüklerin de, eline sağlık. Ama isimler ve konumlar ağdaki herkese açık olacağı için iki şey eklesek iyi olur: kutu sahibinin tek bir kaydı silebilmesi, bir de sayfada kayıtların doğrulanmadığını, herkesin başkası adına yazabileceğini belirten bir not. Bunlar eklenince deneyip karar veririm.

Great idea, and nice thinking on the abuse protection. But since names and locations will be visible to everyone on the network, I think we need two things: a way for the box owner to delete a single entry, and a note on the page saying entries aren't verified and anyone can post on someone else's behalf. Once those are in, I'll test it and decide.

…e unverified

Pano: kutu sahibi tek kaydı listeleyip silebilir, kayıtların doğrulanmadığı notu
@Proxy34

Proxy34 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

İkisini de ekledim:

  • sudo ./scripts/pano.sh kayitlar Ayşe eşleşen kayıtları numarasıyla listeler, sudo ./scripts/pano.sh sil <numara> tek kaydı siler.
  • "Birini ara" bölümünün en üstüne not: "Kayıtlar doğrulanmaz. Herkes başkası adına yazabilir; bir kayıt o kişinin gerçekten güvende olduğunu kanıtlamaz. Mümkünse kişinin kendisine ya da yakınlarına ulaşarak teyit et."

Firewall (Proxy34)

@FurkanKY

FurkanKY commented Nov 3, 2026

Copy link
Copy Markdown

Elinize sağlık, kötüye kullanım önlemleri ve görünmez karakterlerin isprintable ile elenmesi çok iyi düşünülmüş. pano-api.py'yi yerelde çalıştırıp denerken bir veri kaybı yolu buldum:

Dosya bozulursa bütün kayıtlar gidiyor. load() dosya okunamazsa sessizce [] dönüyor. Denedim: 3 kayıt ekleyip guvendeyim.json'u yarıda kestim; servis 0 kayıtla açıldı ve ilk yeni kayıtta save() dosyanın üzerine yazdı, eski 3 kayıt yedeksiz gitti. Afette elektrik kesintisi ve SD kart hatası olağan olduğu için önemli olabilir. Öneri: okunamayan dosyayı guvendeyim.json.bozuk-<zaman> olarak kenara almak, save()'de de os.replace öncesi f.flush(); os.fsync(f.fileno()).

İki not daha:

  • Pano dolunca gerçek kayıtlar düşüyor. MAX_ENTRIES'i 5'e indirip farklı IP'lerden 5 kayıt ekleyince ilk (gerçek) kayıt silindi. IP değiştirerek panoyu dolduran biri gerçek kayıtları düşürebilir; dolunca yeni kaydı reddetmek daha güvenli olabilir.
  • Yazma süresi: 20.000 kayıtta dosya 10,8 MB ve her yeni kayıtta tamamı yeniden yazılıyor; bu bilgisayarda bir kayıt eklemek 270-380 ms sürdü, bu sırada aramalar kilidi bekliyor. Veri /var/lib'de, yani SD kartta. Pi'de ölçmedim.

Küçük bir şey: arama alt dize ile yapıldığı için ali araması Halil'i de buluyor; kelime başında eşleştirmek yeterli olur.


Nice work; the abuse limits and stripping invisible characters with isprintable are well thought out. Running pano-api.py locally I found a data-loss path:

A corrupted file wipes every entry. load() silently returns [] when the file can't be parsed. I added 3 entries and truncated guvendeyim.json; the service came up with 0 entries and the first new entry made save() overwrite the file, so the 3 earlier entries were gone with no backup. Power cuts and SD card errors are routine in a disaster, so this may matter. Suggestion: move an unreadable file aside as guvendeyim.json.bozuk-<time>, and call f.flush(); os.fsync(f.fileno()) before os.replace in save().

Two more notes:

  • Real entries are dropped when the board is full. With MAX_ENTRIES set to 5, adding 5 entries from different IPs removed the first (real) one. Someone rotating IPs could flood the board; refusing new entries when full may be safer.
  • Write time: at 20,000 entries the file is 10.8 MB and is rewritten on every new entry; one add took 270–380 ms on my PC while searches wait on the lock. The data lives in /var/lib, i.e. on the SD card. Not measured on a Pi.

Small thing: search uses substring matching, so ali also finds Halil; matching at word starts would be enough.

… word-start search / Pano: satır satır kayıt, bozuk dosya yedeklenir, dolunca reddeder, kelime başı arama
@Proxy34

Proxy34 commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

@FurkanKY yerelde çalıştırıp bu kadar ayrıntılı denediğin için teşekkürler. Dördü de haklıydı, hepsini düzelttim (37d33cb).

  1. Kayıtlar artık satır başına bir kayıt olarak tutuluyor; her yeni kayıt dosyanın sonuna ekleniyor ve diske yazıldığı (fsync) doğrulanıyor. Elektrik kesilirse en fazla o an yazılan kayıt gidiyor. Açılışta okunamayan satır bulunursa dosya önce guvendeyim.jsonl.bozuk-<zaman> olarak yedekleniyor, sonra okunabilen kayıtlarla yeniden yazılıyor; böylece yeni kayıt yarım kalmış satırın arkasına eklenmiyor. Senin denemeni tekrarladım: 3 kayıttan sonra dosyayı yarıda kestim, servis 2 kayıtla açıldı, yedek oluştu, sonra eklenen kayıt da yeniden açılışta duruyordu.
  2. Pano dolunca eski kayıtlar artık silinmiyor; yeni kayıt "Pano dolu, kutu sahibine haber verin" mesajıyla reddediliyor.
  3. Yeni kayıt artık dosyanın tamamını yeniden yazmıyor. 20.000 kayıtta (9,7 MB) bir kayıt eklemek bu bilgisayarda 6 ms sürdü. Dosyanın tamamı yalnızca bir kayıt silinince yeniden yazılıyor.
  4. Arama kelime başından eşleşiyor: "ali" "Ali Yılmaz"ı buluyor, "Halil"i bulmuyor. Tire ve kesme işareti de kelime ayracı sayılıyor, "ozturk" "Kaya-Öztürk"ü buluyor.

Kutu sahibinin pano.sh kayitlar / sil komutlarını da yeni biçime göre güncelleyip sanal makinede denedim.

Firewall (Proxy34)

@schematrix

Copy link
Copy Markdown
Owner

Merhaba Proxy34, bu PR'ı bugün gerçek kutuda, Pi 5 üzerinde baştan sona denedim. Kod çok özenli yazılmış. Servisin sadece 127.0.0.1'i dinlemesi, DynamicUser ile yetkisiz çalışması, IP'nin nginx'ten alınması, girdilerin kaçış karakterleriyle temizlenmesi ve silme kodunun hash olarak saklanması gerçekten çok iyi. FurkanKY'nin bulduğu sorunları da çok güzel çözmüşsün.

Ama gerçek kutuda sanal makinede çıkmayan bir hata bulduk ve bu tam olarak gerçek donanımda test etmenin neden önemli olduğunu gösteriyor. Bir kayıt ekledim, sonra "sudo ./scripts/pano.sh sil " ile sildim. Bu komut guvendeyim.jsonl dosyasını root olarak yeniden yazıyor ve dosyanın sahibi root oluyor. Servis DynamicUser ile çalıştığı için o andan sonra her yeni kayıtta "PermissionError: Permission denied" alıyor ve pano kimse fark etmeden yazılamaz hale geliyor. Afette kutu sahibinin tek bir uygunsuz kaydı silmesi bütün panoyu durdurabilirdi. Dosyanın sahibini elle düzeltince her şey düzgün çalıştı. Kayıt, Türkçe arama, kodla silme ve saatler sorunsuzdu.

Çözüm olarak pano.sh sil, yeni dosyayı yazdıktan sonra os.replace'ten önce eski dosyanın sahibini ve iznini kopyalayabilir, örneğin st = os.stat(dosya), sonra os.chown(tmp, st.st_uid, st.st_gid) ve os.chmod(tmp, st.st_mode). Bir de sunucu hata verdiğinde sayfa "Pano şu anda kapalı" diyor. Kullanıcı panonun kapatıldığını sanıyor. Yazma hatası için ayrı bir mesaj olursa daha anlaşılır olur.

Son olarak #11, #16 ve #17 bu PR'dan sonra merge edildiği için README.md, install.sh, scripts/install-homepage.sh ve www/home/index.html dosyalarında çakışma var. Bunları düzeltip güncel main'e göre yenileyebilirsen kutuda bir kez daha deneyip merge etmek istiyorum.

IMG_6940

Hi Proxy34, today I tested this PR end to end on the real box, a Pi 5. The code is written with real care. The service listening only on 127.0.0.1, running unprivileged with DynamicUser, taking the IP from nginx, escaping all input and storing the delete code as a hash are all very well done. You also solved the issues FurkanKY found really nicely.

But on the real box we found a bug that didn't show up in the VM, and it's exactly why testing on real hardware matters. I added an entry and then deleted it with "sudo ./scripts/pano.sh sil ". That command rewrites guvendeyim.jsonl as root, so the file ends up owned by root. Since the service runs as a DynamicUser, every new entry after that fails with "PermissionError: Permission denied", and the board silently stops accepting entries. In a disaster, the box owner removing a single inappropriate entry could take the whole board down. After I fixed the file owner by hand, everything worked fine. Adding entries, Turkish search, deleting with the code and the timestamps were all correct.

As a fix, pano.sh sil could copy the old file's owner and mode before os.replace, for example st = os.stat(path), then os.chown(tmp, st.st_uid, st.st_gid) and os.chmod(tmp, st.st_mode). Also, when the server returns an error the page says "Pano şu anda kapalı" (the board is closed), so users think it was switched off. A separate message for write errors would be clearer.

Finally, #11, #16 and #17 were merged after this PR, so there are conflicts in README.md, install.sh, scripts/install-homepage.sh and www/home/index.html. If you can fix these and rebase on the current main, I'd like to test it on the box once more and merge it.

# Conflicts:
#	README.md
#	install.sh
#	scripts/install-homepage.sh
#	www/home/index.html
…isk write fails / Pano: silmede dosya sahibi korunur, diske yazılamazsa açık mesaj
…Ana sayfa menüsü: telefonda satır sararken bağlantılar bölünmez
@Proxy34

Proxy34 commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

@schematrix gerçek kutuda denediğin ve hatayı bu kadar net anlattığın için çok teşekkürler. Haklısın, sanal makinede sil komutunu servisin kendi dosyasında denemediğim için kaçırmışım.

  • pano.sh sil artık yeni dosyaya eski dosyanın sahibini ve iznini kopyalıyor (önerdiğin gibi os.stat, os.chown, os.chmod). Bu sefer servisin gerçek dosyasıyla denedim: kayıt ekledim, sudo ./scripts/pano.sh sil ile sildim, dosyanın sahibi servis kullanıcısı olarak kaldı ve sonraki kayıtlar sorunsuz eklendi.
  • Servis diske yazamazsa artık "Kayıt diske yazılamadı, kutu sahibine haber verin" diyor ve hatayı journalctl -u has-pano-api günlüğüne yazıyor. "Pano şu anda kapalı" yalnızca servis gerçekten kapalıyken çıkıyor. Dosyanın sahibini bilerek root yaptım: bu mesaj çıktı, liste okunmaya devam etti, sahibi düzelince kayıt yine eklendi.
  • Güncel main'i birleştirip çakışmaları çözdüm. Menüye Güvendeyim bağlantısı eklenince telefonda menü iki satıra sarıyor ve "»" işareti bir satırda, bağlantının adı öbür satırda kalabiliyordu. Bağlantıları inline-block yaptım; 320, 360 ve 390 px genişlikte kontrol ettim.

Firewall (Proxy34)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants