SnowIT

IT consulting company — Sarajevo operations, contracts, clients.

Overview

SnowIT Overview

IT consulting company — Sarajevo operations, contracts, clients.

Owner: John Last Verified: 2026-02-17

Contents

To be populated from SnowIT company documentation

Active Tasks

Last Verified: 2026-02-17 | Owner: John

SnowIT — Active Tasks

Open Tasks

Medium Priority

Recently Completed

Planned Work

Key Decisions

Last Verified: 2026-02-17 | Owner: John

SnowIT — Key Decisions

Strategic Decisions

Market Focus: Bosnia & Herzegovina (2026-02)

Decision: SnowIT targets B&H market exclusively (snowit.ba domain). Rationale: Local market knowledge, language advantage, untapped AI services market. Implementation: Website in Bosnian/Serbian/Croatian, local pricing, B&H payment methods.

Service Packaging (2026-02)

Decision: Offer 3-tier packages (Digital Presence 800 KM, 2-4h response) Rationale: Predictable pricing, easier sales, scalable delivery. Source: Financial model from AI agent (finance).

Content Marketing Strategy (2026-02)

Decision: 3 posts per week on LinkedIn (12-13 posts/month) Rationale: Consistent presence, thought leadership, organic reach. Source: Content calendar from AI agent (marketer).

Partnership Model (2026-02)

Decision: Collaborate with Asmir as partner/sales contact. Status: Active, awaiting LinkedIn activity confirmation.

Technical Decisions

Website Stack

Sales Process

Operational Decisions

Pricing Strategy

Project Overview

Last Verified: 2026-02-17 | Owner: John

SnowIT — Project Overview

What is SnowIT?

SnowIT (snowit.ba) is an AI-driven agency focused on the Bosnia & Herzegovina market. It provides software development, design, security, data, infrastructure, and marketing services under one roof.

Current Status

Recent Work

Services Offered

  1. Software Development — Web, mobile, custom applications
  2. Design — UI/UX, branding, visual design
  3. Security — Infrastructure security, compliance
  4. Data — Analytics, business intelligence
  5. Infrastructure — Cloud hosting, deployment
  6. Marketing — Digital marketing, content, campaigns

Tech Stack

Contacts

Key Documents

Specifications Index

Last Verified: 2026-02-17 | Owner: John

SnowIT — Specifications Index

Business Documents

Sales & Marketing

Service Catalog

Located at snowit.ba/usluge:

  1. Software Development
  2. Design Services
  3. Security Consulting
  4. Data Analytics
  5. Infrastructure Management
  6. Digital Marketing

Technical Documents

Website

Infrastructure

Planning Documents

SnowIT Tenant Tree (2026-05-15 migration)

SnowIT Tenant Tree (2026-05-15 migration)

SnowIT BA is an independent legal entity. ALAI Holding AS provides technology and operations support under service agreement. ALAI has ZERO financial share/equity in SnowIT BA.

CEO directive 2026-05-15: "SnowIT BA = independent legal entity. ALAI = tech-only ZERO financial share."

New Canonical Layout

As of 2026-05-15, SnowIT BA operates under dedicated tenant tree:

~/tenants/SnowIT-BA/
├── company/           # Corporate operations
│   ├── state/         # System state, sessions (was ~/clients-external/snowit-state)
│   ├── finances/
│   ├── contracts/
│   └── reports/
├── legal/             # Legal documents, compliance
├── contacts/          # CRM, stakeholder data
├── calendar/          # Events, deadlines
├── mail/              # Email archives, campaigns
├── forms/             # Templates, intake forms
├── templates/         # Document templates
├── web/               # Web properties
│   └── snowit-site/   # Main repo (was ~/clients-external/snowit-site)
└── clients/           # SnowIT client projects

Migration Summary

What Moved

What Stayed in ALAI Tree

Hard Cutover Confirmed

Both old directories removed:

Validation Evidence

Status: PASS (9/9 criteria)

IDCriterionStatusEvidence
V1snowit.ba serves HTTP/2 200✓ PASS/tmp/proveo-100723/curl-snowit.txt
V2enterprise.snowit.ba serves HTTP/2 200✓ PASS/tmp/proveo-100723/curl-enterprise.txt
V3Playwright screenshot rendered✓ PASS/tmp/proveo-100723/snowit-landing.png (124KB)
V4Vercel projectId intact✓ PASS/tmp/proveo-100723/vercel-project.json
V5LaunchAgents operational✓ PASS3 daemons loaded, path-independent
V6Git remote correct✓ PASSgit@github.com:snowitba/snowit-site.git
V7Repo-local [user] config removed✓ PASSGlobal ~/.gitconfig inherits correctly
V8No live functional refs to old path✓ PASS20 mentions are historical/spec/memory only
V9Hard cutover confirmed✓ PASSBoth old directories removed

Open Items

Non-Blocking

References

SEO Readiness Portal Cloud Migration — 2026-06-01

SEO Readiness Portal Cloud Migration — 2026-06-01

Date: 2026-06-01 Owner: john Verdict: DONE

Live state verified (2026-06-01 05:31 UTC)

No traffic to John local host: cloudflared route for seo-tools.alai.no and seo-tools.snowit.ba set to http_status:503.

Architecture (now)

Browser → Cloudflare Access (seo-tools.alai.no, alai-no team) → Cloudflare proxied DNS → Azure App Service Linux container seo-readiness-alai (Sweden Central, rg rg-seo-readiness-prod, asp asp-seo-readiness-prod B1) → Next.js standalone in alairegistry.azurecr.io/seo-readiness-portal:20260531-cloud (digest sha256:16c8a40a...) → persistent /home/data/workspace.json

App access mode: SEO_PORTAL_ACCESS_MODE=cf-access, trusted header CF-Access-Authenticated-User-Email, allowed domains snowit.ba,alai.no, extra allowed alembasic@gmail.com.

Evidence

Docs corrected (no localhost as final target)

Rollback

If Azure origin needs rollback:

  1. Revert Web App container image: az webapp config container set -g rg-seo-readiness-prod -n seo-readiness-alai --container-image-name alairegistry.azurecr.io/seo-readiness-portal:<previous-tag>
  2. CF Access policy and DNS remain unchanged; no public bypass introduced.
  3. Do NOT re-enable cloudflared local route — that violates the CEO correction.

Open follow-ups (separate MCs, not blockers)

CEO scope check

MC / evidence references


See Also — Agent Runbook

The canonical end-to-end workflow runbook (intake to John deep-report, anti-pitfalls, trigger checklist):

QODY CI-CD pipeline fixes 2026-07-08 (MC 105057+105059)

QODY CI/CD demo pipeline fixevi — 2026-07-08 (MC #105057 + #105059)

Dokazano zelenim pipeline run #330 (commit 747c100, deployDemo=true). John live-verifikovao timeline + endpointe; Proveo nezavisna tool-verifikacija PASS.

Kontekst

Run #328 (buildx fail) i raniji Trivy fail (#327) blokirali QODY demo deploy. Oba uzroka su bili pre-postojeće pipeline rupe, ne aplikacijski/Monri kod.

Fix #105059 — idempotent buildx (Build_Images)

Fix #105057 — Trivy verify-not-fetch (Security_Image_Scan)

Dokaz (run #330)

Stage Rezultat
CI Gates succeeded
Build_Images → ACR succeeded (buildx fix radi)
Security: Trivy image scan (4 images) succeeded (Trivy fix radi)
Deploy → QODY demo ACA succeeded
Deploy_Prod skipped (ispravno — nije main)

Timeline: 165 succeeded / 0 failed / 6 skipped (prod).

Live (John curl): demo.api.qody.ba/health → 200 (db.connected, RLS PASS); demo.app.qody.ba → 200.

Evidence: ~/system/evidence/105048/pipeline-330-timeline.json + verdict-105057.json + verdict-105059.json

QODY Monri WebPay integracija — E2E dokaz (MC 105048)

MC #105048 - Monri WebPay Integration - Final E2E Proof

Result: SUCCESS - real sandbox payment completed end-to-end, guest-facing

error page RESOLVED (no longer cosmetic)

Orders proven paid (3 separate live sandbox transactions this session):

Flow proven live (Playwright, e2e/monri-sandbox-checkout.test.js)

  1. Guest lands on demo.app.qody.ba (venue "kafana", Table 1)
  2. Adds item to cart (Brusketa, 8.50 BAM)
  3. Submits order (POST /guest/order - 200)
  4. Proceeds to checkout, selects "Plati odmah" (pay now)
  5. POST /guest/payment/intent - 200, provider=monri, real Key-Vault-sealed credentials (not placeholder)
  6. Monri Lightbox script (class="lightbox-button", ch_* customer fields) renders "Pay with card" button - screenshot 07
  7. Clicks through to Monri's real hosted iframe (ipgtest.monri.com/v2/payment/.../form)
    • screenshot 08
  8. Fills real Monri sandbox test card (4058400000000005, exp 12/30, cvv 123) via keystroke simulation (pressSequentially, not fill - masked-input fix)
    • screenshot 09
  9. Submits - card validates, Monri approves, triggers redirect - screenshot 10
  10. Monri's server-to-server Callback (webhook) arrives at /webhooks/payment/monri, resolves venue, verifies signature (valid SHA512(merchant_key+body) digest, WP3-callback scheme), marks payment succeeded - confirmed via qody-api logs (200 OK, 36ms)
  11. Order flips to payment_status=paid in Postgres - confirmed via psql (3x, see order IDs above)

405 Not Allowed after payment - RESOLVED (was previously mis-called "cosmetic")

This was correctly flagged as a real launch-blocker, not cosmetic: a real guest who pays would have seen nginx's raw "405 Not Allowed" error page immediately after paying - even though the order was genuinely marked paid via the webhook, the guest had no way to know that. This is the same "deploy 200 hides dead flow" trap this session avoided elsewhere, applied to the guest-facing side.

Root cause and fix, found through 3 live-verified iterations (code review alone could not have caught any of these - Monri's actual runtime behavior differs from what the docs literally show):

  1. Attempt 1 (insufficient): added preventDefault() in a 'submit' event listener on the payment form. Deployed clean, but Monri's lightbox.js calls form.submit() programmatically, which bypasses all 'submit' event listeners entirely (only .requestSubmit() fires them) - a genuine DOM API quirk, invisible from code review.
  2. Attempt 2 (partial fix, new bug): pointed the form's action at a real server-side route per Monri's documented pattern. This fixed the 405, but introduced a cross-origin CORS 403 (demo.app.qody.ba POSTing to demo.api.qody.ba) - Ktor's CORS plugin rejects the Origin header on a plain HTML form POST even when the origin is in the allowed list.
  3. Attempt 3 (working, verified live 2x): made the redirect target same-origin - added an nginx location = /payment/monri-return { return 303 /; } block on the guest app's own domain, and pointed the form's action at the relative path /payment/monri-return. Verified via curl -X POST https://demo.app.qody.ba/payment/monri-return -> 303, and via 2 full live Playwright E2E re-runs: card validated, payment succeeded, redirect worked (no 405, no 403, FINAL_URL: https://demo.app.qody.ba/), and both resulting orders (7419eee0, 6bf5ef27) confirmed paid in Postgres.

Guest sees the normal venue menu after redirect, not an error page. The order is genuinely paid. Guest does not yet see an explicit "payment confirmed" screen on that same redirect (they'd need to re-check the order status manually, e.g. via the handover QR flow) - that gap is a separate, non-blocking UX improvement, tracked as MC #105069 (session-resume across the redirect so OrderStatusPage's existing confirmation UI shows automatically). A candidate fix for #105069 is already built and committed (commit 0a525e5, branch feat/qody-monri-checkout-105048) but is out of scope for #105048's launch-blocker bar per team-lead decision.

Negative webhook test (webhook-negative-test.txt)

POST /webhooks/payment/monri with Authorization: WP3-callback -> HTTP 400 {"error":"Invalid Monri webhook signature"}

Bugs found and fixed during this live E2E session (none catchable by

static review or isolated unit tests - required a real transaction against

Monri's actual sandbox server):

  1. class="lightbox-button" missing on injected script tag (c94a640)
  2. ch_full_name/ch_address/ch_city/ch_zip/ch_country/ch_email/ch_phone customer fields required by lightbox.js validation (caf30df)
  3. Digest must use merchant "Key" (Kljuc), not authenticity_token - inverted from the isolated docs example (e38e9c0)
  4. "N/A" placeholder rejected as invalid ch_address/ch_phone (1132edd)
  5. ch_phone must be local format, no country-code prefix (74ae0ff)
  6. 405 Not Allowed post-payment - 3-round fix, see section above (3c296b8, d2e1d78, d38935e)
  7. Test-harness-only: Playwright .fill() bypasses Monri's input mask; switched to pressSequentially() (b1bda91, e2e test file)

Infra issues found and fixed during this session (separate from Monri code)

Merchant configuration change (Monri sandbox, Asmir's account)

Set "Callback URL" on merchants/4988 (SnowIT) to https://demo.api.qody.ba/webhooks/payment/monri - was blank before, which is why no webhook could ever have arrived prior to this fix. This is the webhook delivery destination, required for any Monri integration to work at all, sandbox or production. Full detail, including confirmation that Asmir's password was NOT touched: see asmir-account-changes.md.

Backend test suite

277/277 tests passing, 0 failures, 0 errors - re-verified fresh this turn via ./gradlew test (JDK 21, apps/api), BUILD SUCCESSFUL, all tasks UP-TO-DATE (no backend/Kotlin changes since the last full run - commit 0a525e5 only touches frontend TypeScript). Cross-checked against CI pipeline run 330, which independently ran "Backend: Gradle tests (Java 21)" -> succeeded and "Backend: integration tests (Testcontainers)" -> succeeded.

Final commit and branch

Branch: feat/qody-monri-checkout-105048 Latest commit: 0a525e5 (session-resume candidate fix for #105069 - not required for #105048's launch-blocker bar, but harmless and already built)

Full commit list (oldest to newest):

QODY Admin UX Redesign — ZAVRSEN (MC 105067)

QODY Admin + Super-Admin UX Redesign — ZAVRŠEN (MC #105067)

Datum: 2026-07-08 | Vodio: Vizu (Brad Frost + Lea Verou), UX validacija Angie Jones (Proveo), sigurnost Parisa Tabriz (Securion) Živo: demo.admin.qody.ba (revizija qody-admin--uxfinal801b2d7, 100% traffic) | Branch feat/qody-admin-design-system-105067, commit 801b2d7

Root cause (zašto je bilo loše)

Onboarding wizard i super-admin (#104856) vođeni kao "Petter Graff lead" — arhitekta, ne dizajner. Nijedan UI/UX dizajner nikad nije bio glavni; UI bio Phase-2 dodatak inženjerskog toka. Rezultat: nijedan design system — 19 admin views (11.660 linija) svaki hand-roll inline stilove; super-admin "kreiraj lokal" bio flat 7-poljni 480px modal bez wizard strukture ni a11y.

Šta je urađeno (CEO odluka: čisto frontend, backend contract netaknut)

Tier A — design system + super-admin wizard:

Tier B — admin dashboard: VenuesView, MenusView (popravljeni pravi focus-trap/ESC na modalima), SettingsView + StaffView + ReportsView (bio 100% raw hex) + TablesView — svi na design system.

Tier C — super-admin konzola: Transactions/Refunds/Subscriptions/Audit tabovi → q-data-table + Button komponenta, ~40 raw hex → tokeni.

Verifikacija (sva 3 gejta + bugfix)

Napomene

QODY Phase 1 Provisioning Gate + Integracioni Deploy (MC 105075+105077)

QODY Phase 1 Provisioning Gate + Integracioni Deploy — ZAVRŠENO (MC #105075 + #105077)

Datum: 2026-07-08 | Live demo: rg-qody-demo, revizije int105077 (api--int105077b, guest--int105077, admin--int105077), sve 100% traffic, sve 200.

Šta je deployano (integrisano, jedan konzistentan build)

Spojene 3 feature-grane u jedan qody-api/guest/admin build (merge branch feat/qody-105077-integration, commit 27a8250):

Migracije: V25 → V26_monri → V27_provisioning_gate (čist redoslijed).

Concierge model (CEO odluka 2026-07-08) — LIVE

Proveo cross-role UAT + adversarial bypass — PASS 6/6 (raw HTTP, teži od browsera)

  1. Provisioning flow end-to-end: onboard→invite→menu→tables→hours→cash-only→activate ACTIVE ✓
  2. Checklist enforcement: activate prije kompletiranja → 422 missing[] (server-side) ✓
  3. Owner lock: 403 non-ACTIVE, 200 nakon aktivacije ✓
  4. Adversarial bypass (kritično): owner JWT → /admin/ non-ACTIVE = 403; owner → /superadmin/ = 403 (ne može escalirati); impersonation PENDING = blokiran** ✓
  5. Regresija: Monri webhook 400 (živ), guest menu, health/RLS PASS — NEregresirano ✓
  6. Cash-only reaches ACTIVE bez Monri ✓

Incident tokom deploya (uhvaćen, vraćen)

Prvi pokušaj: api build iz pogrešnog konteksta (repo-root umjesto apps/api) → deployao mrtav image → qody-api health 000 → rollback na 0000080 (Monri) u minuti → rebuild iz ispravnog apps/api konteksta → uspjeh. Ranije: gate-only api (iz main, bez Monri) regresirao Monri → rollback. Pouka: qody-api build-grana MORA imati sve žive feature; api build kontekst = apps/api.

Follow-up (ne-blokirajuće)

Evidence: ~/system/evidence/105077/uat/uat-report.md

QODY Demo — MASTER Bug Registar (pun cross-role UAT 2026-07-08)

QODY DEMO — MASTER BUG REGISTAR (kompletan cross-role UAT, 2026-07-08/09)

8 agenata (4 happy-path po ulozi + 4 kombinatorne matrice). 7/8 gotovo; kitchen KDS pending (blokiran reset-pw bugom — sam po sebi nalaz). Evidence po agentu u ~/system/evidence/uat-full/{owner,guest,superadmin,matrix-A-perms,matrix-B-state,matrix-C-payment,matrix-D-edge}/.

🔴 CRITICAL / BLOKERI (5)

C1 — Cross-ORG data leak (#105076) [backend, sec] — POTVRĐEN LIVE iz 2 ugla (matrix-A + owner). GET /admin/venues + /admin/venues/{id} vraćaju SVE venue-ove SVIH organizacija bilo kojoj autentifikovanoj sesiji (bilo koja uloga, nema role/tenant check). Leak: fee, branding, slug cross-org. Root: AdminRoutes.kt:74-99 (fix pattern već na PUT 101-118). NAJVIŠI prioritet.

C2 — Reset-password modal PRAZAN [frontend] — owner + kitchen UAT. POST /admin/staff/{id}/reset-password vraća 200 s temporaryPassword, ali UI modal display div prazan, "Kopiraj" kopira ništa. Owner nema način dobiti novi pw. (Ranija "PASS" verifikacija gledala network 200, ne UI prikaz — lekcija.) Blokirao i kitchen UAT.

C3 — Monri plaćanje mrtvo [external/config] — guest UAT. Monri injected iframe.js pravi malformed URL "ipgtest.monri.com:/v2/" (kolon bez porta) → iframe se ne učita → guest ne može platiti, checkout dead-end. Naš kod (lightbox.js src) ispravan. Treba istražiti Monri merchant config/authenticity token (možda loš base URL u Monri postavkama).

C4 — Add-to-cart prekriven AI-chat FAB-om [frontend] — guest UAT (mobile). "DODAJ" dugme fizički ispod "Pitajte nas" FAB-a → tap otvara chat umjesto dodavanja. Blokira naručivanje na telefonu. Fix: z-index/pozicija.

C5 — SUSPEND je lažan [backend] — matrix-B. suspend samo set deleted_at, NE provisioning_state (koji gate provjerava). Suspendovan venue: owner postojeća sesija radi (200), GUEST NASTAVLJA naručivati (GuestRoutes bez provjere). SUSPENDED+PENDING_PAYMENT stanja dokumentovana ali nikad build-ana.

🟠 MAJOR (3)

M1 — Suspend/activate bez potvrde [frontend/superadmin] — misklik mijenja live status merchanta, nema "jesi siguran", nema undo. M2 — Audit log ne bilježi activate/suspend [backend] — status se tiho flipne bez traga (kombinovano s M1 = opasno). M3 — Raw error poruke korisniku [frontend/backend] — (a) dupli slug → raw PSQLException 500 u UI (leak DB constraint, izgleda kao crash); (b) super-admin wrong-pw → raw JSON "401:{...}" u banneru; (c) merchant wrong-pw → netačno "session expired". Login banner postoji (Vizu fix) ali sadržaj sirov.

🟡 MEDIUM (5)

🟢 LOW (7)

ČISTO / RADI (verifikovano)

Gate PENDING→ACTIVE airtight (12 ruta 403, cash-only ACTIVE radi); XSS escaped svuda; price/email/empty validacija; JWT verify + brute-force lockout; superadmin rute odbijaju owner (403); menu CRUD; QR gen/regen; operating hours; tips; CSV export; 4-jezik switch (osim navedenih); logout/session invalidation; modal focus-trap/ESC.

HOUSEKEEPING

~15 leftover UAT test orgova + XSS-probe org u demo listi → cleanup (#105068).

BATCH FIX PLAN (nakon konsolidacije → jedan merge → jedan deploy → re-UAT)