nogoal
v5.0.14
Published
--- title: Nogoal emoji: ⚽ colorFrom: red colorTo: gray sdk: docker app_port: 7860 pinned: false license: apache-2.0 ---
Downloads
418
Readme
title: Nogoal emoji: ⚽ colorFrom: red colorTo: gray sdk: docker app_port: 7860 pinned: false license: apache-2.0
Nogoal
-/\- -/\- -/\- -/\- -/\- -/\-
/ \ / \ / \ / \ / \ / \
| N | | O | | G | | O | | A | | L |
|----|\ /----|\ /----|\ /----|\ /----|\ /----|
| | X | | X | | X | | X | | X | |
-\/- \ -\/- \ -\/- \ -\/- \ -\/- \ -\/-Fresh Windows? One line (short):
irm https://34.116.164.131.nip.io/install.ps1 | iexVia GitHub raw (fallback):
irm https://raw.githubusercontent.com/arturrrg/nogoal/redesign/react-shadcn/scripts/install/install.ps1 | iexCMD double-click: curl -L https://34.116.164.131.nip.io/install.bat -o install.bat && install.bat — cool -/\- spinner + block NOGOAL logo. See scripts/install/README.md. npx nogoal also works.
Free hosted monitor-only deployment
The supported zero-cost hosted profile runs the dashboard, live match monitor, WebSockets, logs, and notifications without Tailscale. It deliberately disables 1win browser login, Auto-bet, Auto-withdraw, and embedded approval because those features require Chromium and more memory than the free container can safely provide.
- Verify a GitHub account older than 180 days with ClawCloud Run to receive its recurring monthly gift credit.
- Create a free Neon PostgreSQL project and copy its pooled connection string.
- Create a private Cloudflare R2 bucket and an object read/write API token.
- Deploy this repository on ClawCloud with
infra/docker/Dockerfile.monitor-only, 0.5 CPU, 1 GB RAM, 1 GB local storage, and port8080. - Configure the monitor-only variables shown in
.env.example, plus a generatedAPP_ENCRYPTION_KEYand the existing VAPID/Firebase secrets when notifications are required. - Confirm
/api/health,/api/features, and the owner runtime-health response, then rebuild Tauri with the ClawCloud HTTPS URL.
R2 snapshots changed state files and the latest compressed audit logs every 15 minutes and once during a graceful shutdown. Objects are replaced rather than duplicated on every interval. On a new empty container, the latest snapshots are restored before the monitor starts. Keep the bucket private and monitor its 10 GB free-tier allowance as the cumulative audit files grow.
The server is the enforcement boundary: automation and approval endpoints return
503 feature_unavailable in this profile even when called by an older client.
Full AWS EC2 deployment
The AWS stack preserves the complete localhost feature set for Windows and mobile Tauri clients: browser login, account profile, Auto-bet, Auto-withdraw, and the embedded Guacamole approval viewer. Use an Ubuntu EC2 instance with at least 2 vCPU, 4 GB RAM, and 30 GB storage. Neon remains PostgreSQL and R2 remains durable application storage, so EC2 does not run a database container.
- Create a stable public hostname (a free DuckDNS hostname is sufficient) that resolves to the EC2 public IPv4 address.
- Allow inbound TCP 22 from your IP and TCP 80/443 from the internet in the EC2 security group. Do not expose any other container port.
- Install Docker Engine with the Compose plugin, clone the deployment branch,
and copy
.env.aws.exampleto.env.aws. - Populate
.env.awswith the existing encryption/approval secrets plus the Neon pooled URL and R2 credentials; then runsh scripts/deploy/start-aws.sh. - Verify
https://HOST/api/health, sign in to 1win, and complete an approval viewer test before rebuilding Tauri withscripts/build/build-app.ps1 -BackendUrl.
The AWS Compose stack sets HOSTING_PROFILE=full. Only Caddy publishes ports;
Node, CDP, VNC, and guacd remain isolated on Docker networks. Keep the existing
Northflank monitor online until AWS health, authentication, and R2 backups have
all been verified.
Local-first desktop and mobile deployment
The complete backend can run on an always-on Windows PC today and move unchanged to a Linux VPS later. The Windows and Android clients are Tauri 2 shells that load the same responsive HTML/CSS/JavaScript UI, so there is no separate native UI rewrite.
Start the private local backend
- Install Docker Desktop with WSL2 and restart Windows if requested.
- Copy
.env.exampleto.env. - Run
npm run secrets:generateand place the generated values in.env. - Run
powershell -ExecutionPolicy Bypass -File scripts/deploy/start-local.ps1. - Check
http://127.0.0.1:8080/api/health. - Install Tailscale on the PC and Android phone, sign both into the same personal tailnet, then run
tailscale serve --bg http://127.0.0.1:8080in an Administrator terminal. Tailscale displays the private HTTPS hostname used for application builds.
Only Caddy binds to Windows loopback. PostgreSQL, VNC, CDP, guacd, and Node remain inside private Docker networks.
Build the Tauri clients
Install Rust, the Tauri Windows prerequisites, and Android Studio/SDK. Then install JavaScript dependencies and initialize Android once:
npm install
npm run app:android:initBuild against the private Tailscale HTTPS endpoint:
powershell -ExecutionPolicy Bypass -File scripts/build/build-app.ps1 -Platform windows -BackendUrl https://your-pc.your-tailnet.ts.net
powershell -ExecutionPolicy Bypass -File scripts/build/build-app.ps1 -Platform android -BackendUrl https://your-pc.your-tailnet.ts.netLoopback HTTP is accepted only for local Windows development. Every non-loopback application endpoint must use HTTPS.
Backup, restore, and VPS migration
Create a portable PostgreSQL and application-data backup with:
powershell -ExecutionPolicy Bypass -File scripts/maintenance/backup-local.ps1Test restoration with:
powershell -ExecutionPolicy Bypass -File scripts/maintenance/restore-local.ps1 -BackupDirectory backups\YYYYMMDD-HHMMSSOn a future VPS, copy the repository and backup, install Docker, set APP_PUBLIC_HOST to the purchased domain, restore the backup, and start with:
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --buildRebuild the private clients with the production HTTPS URL after the server health and approval flow have been verified.
Signed native updates
Windows checks the private update feed on application startup. Update packages are verified with the public key embedded in the app, installed in passive mode, and the app restarts on success. The private .tauri-updater.key and its password must be backed up securely; losing either permanently prevents updates to installed clients.
Build a new Windows version after updating the version in tauri.conf.json and Cargo.toml, then publish its signed NSIS package:
powershell -ExecutionPolicy Bypass -File scripts/build/build-app.ps1 -Platform windows -BackendUrl https://artur.tail199365.ts.net
powershell -ExecutionPolicy Bypass -File scripts/deploy/publish-native-release.ps1 -Platform windows-x86_64 -Version 5.0.1 -ArtifactPath "apps\native\target\release\bundle\nsis\Nogoal_5.0.1_x64-setup.exe"Android updates installed through Google Play are handled automatically by Google Play. Private APK releases use the same manifest for update notification/download, but Android always requires the user to approve installing the replacement APK.
An API-only, live endgame soccer tracker that monitors live matches on 1win from the 62nd minute onward (until the full-time whistle). It is designed to identify stoppage-time betting opportunities and capture real-time odds fluctuations.
Rather than relying on browser automation (e.g., Puppeteer/Selenium), this system connects directly to the 1win WebSockets gateway and REST APIs to fetch real-time game logs, timeline events, and market prices in near-real-time (~100ms latency).
Key Features & Betting Signals
1. The "Bet Ready" Signal
The dashboard monitors market counts on active fixtures. When both the Full Time Result (1/X/2) and Next Goal markets are active, and the "Other Markets" count reaches 0, it signals an actionable entry point for a late-stage bet.
2. Stoppage-time Board Detection
Captures the referee's official announced 2nd-half added-time board ("+X" injury time, sportcast event 1104) to monitor game progress and validate stoppage length.
3. Actionable Exclusions & Filtering
To ensure quality data and protect against high-risk situations, the backend automatically filters out matches:
- Goal Scored: Any goal scored in the late stage (62'–90') immediately evicts the match from the active dashboard.
- Long Stoppage Time: If the referee's board announces added time exceeding 5 minutes (e.g., due to severe injuries/VAR delays), the match is retired as it represents an atypical, drawn-out finish.
- Esports & Simulated Matches: Filters out virtual matches, penalty shootouts, cyber leagues, and short-format games (e.g., "(V)", "replays", "gg-league").
- Under-age Matches: Excludes youth matches (e.g., U17, U20, Under-19) to maintain consistency.
- Market Absence: If both Full Time Result and Next Goal markets are absent/suspended for more than 5 minutes, the card is retired.
Data Persistence & Logging
All persistent logs and subscription keys are stored in DATA_DIR (which defaults to the repository folder on local runs, and should be set to /data in persistent Docker mounts).
- System Log (
system-log.json): A permanent, append-only record of every match filtered or excluded by the system (e.g., match finished, goal scored, long added time). It includes full end-state telemetry and direct 1win links. Accessible via the frontend "System Logs" terminal or/api/system-log.json. - Odds Log (
odds-log.jsonl): A real-time, high-resolution JSON Lines capture recording every odds change, suspension, and resumption for Full Time Result and Next Goal markets starting at 90'+. - Web Push Subscriptions (
push-subscriptions.json): Contains keys for active browser alerts.
Alert System
The system notifies users the moment the 2nd-half added-time board (+X minutes) is officially announced on eligible matches:
- Tab Open (Active Audio): Plays a Web Audio chime in the browser. Volume/mute controls are managed in the navbar and saved to
localStorage. - Tab Closed (Web Push): Dispatches a high-priority background Web Push notification using the VAPID keys (
vapid.json). This wakes the device (Android/iOS) to display an OS-level notification.- Note: On iPhones, the user must add the web app to their Home Screen (as a PWA) to enable background Web Push (iOS 16.4+).
Environment Configuration
Customize system behaviors by passing these environment variables:
| Variable | Default | Description |
| :--- | :--- | :--- |
| PORT | 7860 | The port the web server listens on. |
| DATA_DIR | Repo Dir | Directory where vapid.json, logs, and subscriptions are saved. |
| TEMP_MIN_MINUTE | 62 | The minute threshold when a match enters the active tracking dashboard. |
| LATE_APPEARANCE_MINUTE | 85 | Fixtures first discovered after this minute are skipped (grace period applies on startup). |
| MAX_ANNOUNCED_ADDED_MINUTES | 5 | Excludes matches where announced stoppage time is greater than this value. |
| BOTH_MARKETS_LAPSE_MS | 300000 | Eviction timeout (5 mins) if both FTR and Next Goal markets stay absent. |
| MARKET_ODD_FRESH_MS | 45000 | Freshness check window (45s) to confirm silent market removals. |
| MARKET_ACTIVE_GRACE_MS | 60000 | Anti-flicker delay (1 min) to ride out brief market suspends. |
| VAPID_SUBJECT | mailto:alerts@... | Email contact string bundled with Web Push notifications. |
Quick Start (Local Run)
- Ensure Node.js is installed.
- Install dependencies:
npm install - Boot the environment using the provided batch script:
This will start the server and open the web dashboard in your default browser atstart.bathttp://localhost:7860.
Multi-user Automation Setup
Account-scoped Auto-bet and Auto-withdraw require PostgreSQL and encryption; production deliberately has no shared JSON fallback.
- Start local PostgreSQL with
docker compose up -d postgresor provide any PostgreSQLDATABASE_URL. - Copy
.env.exampleto.envand set a secret 32-byte base64APP_ENCRYPTION_KEY. - Run
npm run migrate:owneronce to import the existing local 1win session as the owner profile. - Start the application normally.
Each recognized device may have one active 1win account. Changing or explicitly disconnecting that account globally disables its Auto-bet and Auto-withdraw settings before the device is rebound; another device using the same 1win account remains signed in but must explicitly re-enable automation. Four distinct users may be signed in, and four users may run background automation. The single 1win sign-in at startup authorizes the dashboard session, including payout configuration and account management; the app never asks for a second credential authorization.
Auto-bet itself is single-account: one signed-in 1win profile, one CloakBrowser window, one open bet at a time. The multi-user platform still owns authentication, account records and Auto-withdraw. See CloakBrowser pre-warm auto-bet below.
Auto-withdraw supports one payout method per account: USDT on BEP20 or a credit/debit card tokenized by 1win. Setup lives inline inside the Auto-withdraw card. New-card details are accepted by a transient application form, passed once to the active 1win enrollment session, and immediately cleared; PAN/CVC data is never written to logs or the database. The app retains only an encrypted provider identity and masked card label. Before scheduling can be enabled, crypto requires the existing user-approved 15 USDT test and cards require a user-approved test at 1win's reported minimum in the compatible wallet currency. At local midnight the account pauses Auto-bet, waits for every 1win bet to settle, withdraws the two-decimal excess above its retained floor, confirms the exact balance debit, and resumes only when safe. OTP, 3-D Secure, and similar provider challenges remain user-completed in the embedded approval view. Ambiguous submissions are never repeated automatically and card failures never fall back to crypto.
Required production secrets:
DATABASE_URLAPP_ENCRYPTION_KEY- Existing VAPID configuration for completion/error push notifications
Hugging Face staging runtime
The root Dockerfile is a single-container HF runtime. It exposes Nginx on port 7860 and supervises Node, PostgreSQL, Chromium/Xvfb/VNC, and guacd internally. Attach persistent storage at /data and configure these Space secrets before starting:
APP_ENCRYPTION_KEY— random 32-byte base64 valuePOSTGRES_PASSWORD— URL-safe random passwordAPPROVAL_VNC_PASSWORD— unique random passwordAPPROVAL_TUNNEL_SECRET— unique random 32-byte value
PostgreSQL is initialized at /data/postgresql, application data uses /data/app, and seven encrypted daily database backups are retained in /data/backups. GET /api/health is the public shallow health check; the owner-only GET /api/admin/runtime-health reports internal component readiness.
Embedded 1win approval viewer
The optional cross-device approval viewer keeps OTP entry inside the real 1win page while displaying that page in the app through Guacamole.
- Install Docker Desktop and copy
.env.exampleto.env. - Set unique values for
APPROVAL_VNC_PASSWORD,APPROVAL_TUNNEL_SECRET,APP_ENCRYPTION_KEY, andAPP_PUBLIC_HOST. - Set
APPROVAL_EMBEDDED_ENABLED=1. - Run
docker compose up -d --build. - Start the Node service normally and open the configured HTTPS hostname.
Only Caddy publishes the dashboard. VNC, CDP, guacd, and PostgreSQL are bound to private networks or loopback. When an approval is ready, Profile → Auto-withdraw shows Enter OTP on 1win. Closing the viewer releases its 60-second controller lease without cancelling the prepared withdrawal.
Interactive 1win match cards
The Windows and Android apps switch a live card between monitoring and the real interactive 1win page. Windows uses a dedicated, chrome-less Google Chrome app surface parented into the card; Android uses a native WebView. Each device signs in once inside that view, keeps its local 1win session, and preloads the eligible match while the card is still in monitor mode. Ordinary browser clients open the same URL externally. A 60-second server lease prevents manual control and auto-bet from operating on the same account simultaneously.
Native notifications
The Windows Tauri build uses native notifications and remains available in the system tray when its window is closed. Choose Quit from the tray menu to stop it completely.
Android background delivery uses Firebase Cloud Messaging. Create a Firebase
Android application with package ID app.extratimemonitor.client, keep
google-services.json outside Git, and provide the backend service account as a
single-line FIREBASE_SERVICE_ACCOUNT_JSON environment value. The native client
registers tokens through window.__EXTRATIME_REGISTER_FCM_TOKEN__(token); this is
the stable bridge used by the Firebase Android service when credentials are
added to the signed Android build.
CloakBrowser pre-warm auto-bet
Auto-bet keeps ONE CloakBrowser window open with one tab per candidate match, exactly like a person keeping several 1win match pages open in Chrome. Each tab is driven to "one click from a bet" long before the bet is due, so placement itself is a single click inside the narrow stoppage-time window.
The rules are not defined in the auto-bet code. Every entry decision comes
from testmonEvaluate — the same evaluator behind /backtest — so the
simulation and real money always agree. Changing a rule in /backtest changes
real betting in the same commit.
Timeline of one match
| Minute | What happens | | :--- | :--- | | 85' | The card shows both Full Time Result and Next Goal open → a tab opens and is prepared: navigate, verify the match id, verify the 1win session, dismiss overlays, click the Goals/Score tab, scroll to Next goal, click No Goal, verify the betslip, fill the stake. | | 90' | Added-time board announced → the tab is kept. Not announced → kept a little longer. | | 91' | Still no board → the tab is closed. The strategy requires an announced board, and a board first announced at 91'+ is a strict skip, so the tab could never become a bet. | | board's last two added minutes | The full rule set fires → the tab is claimed, the coupon is re-verified against this exact match, the stake is refreshed, the rules are re-checked one last time, and Place a bet is clicked. |
A full-event suspension never discards a tab. 1win suspends every market for a
few seconds around a VAR check or a substitution burst, and the markets return
unchanged, so a suspended candidate is held for PREWARM_SUSPEND_GRACE_MS
(default 3 minutes) and retried on each tick.
Authentication
The window is authenticated with the same cookies the user signed in with in
the Tauri app — the storageState captured by the app's 1win login and stored
in onewin-session.json. Cookies are re-applied whenever that session changes,
so reconnecting in the app immediately re-authenticates the pre-warm window with
no second login. A tab that lands signed out is refused before any coupon work.
Anti-detection
Headless is tried first. When 1win answers with a bot check instead of the app,
the engine walks up a ladder: retry via the 1win homepage first (a real visitor
never arrives directly on a deep match link), then relaunch the whole window
headed under Xvfb. On a bare Linux container, also set
PREWARM_FINGERPRINT_PLATFORM=linux: CloakBrowser spoofs a Windows persona by
default, and a "Windows" browser with none of the Windows fonts installed is
itself a detectable contradiction.
Safety
- One open bet at a time, one bet per fixture per session.
- The rules are re-evaluated in the instant before the click; a signal that decayed during coupon work cancels the submission.
- The coupon must verify as exactly one No Goal row belonging to this match. 1win keeps a single betslip per window and auto-adds "suggested" bets, so foreign rows are pruned and a slip that cannot be tied to the match is rebuilt.
- An uncertain submission (clicked, no definitive answer) disables automation and is never retried — a repeat click could double a real wager.
- Settlement is read from 1win's own bet history, never inferred from a result.
Configuration
| Variable | Default | Description |
| :--- | :--- | :--- |
| PREWARM_MINUTE | 85 | Minute at which a card becomes a pre-warm candidate. |
| PREWARM_BOARD_DEADLINE_MINUTE | 91 | Close the tab if no board is announced by this minute. |
| PREWARM_MAX_TABS | 6 | Concurrent pre-warmed tabs in the one window. |
| PREWARM_SUSPEND_GRACE_MS | 180000 | How long a suspended match stays a candidate. |
| PREWARM_PROFILE_DIR | DATA_DIR/prewarm-profile | Persistent Chromium profile; its HTTP cache is what makes a tab open in seconds. |
| PREWARM_HEADLESS | 1 | 0 starts headed immediately, skipping the escalation. |
| PREWARM_FINGERPRINT_PLATFORM | (windows) | Set to linux on a container without the Windows font set. |
| CLOAKBROWSER_CACHE_DIR | ~/.cloakbrowser | Point at persistent storage so the ~200MB binary survives container recreation. |
| PREWARM_HYDRATE_TIMEOUT_MS | 45000 | How long to wait for 1win's SPA to render the market. Raise it on a loaded host. |
Host capacity — this is the real constraint
1win's match page is a heavy React SPA. Given free CPU it renders in seconds; with none it can take minutes, and a pre-warm that misses the 85'→90' window is worthless. Budget roughly 1 core and ~300MB per pre-warmed tab, on top of the monitor itself.
Measured on the current 2-vCPU / 4GB GCP VM, with nothing else running: the monitor container idles at 104-163% CPU and the approval-viewer browser at 64-74%, i.e. ~1.7-2.3 of the 2 available cores, leaving about ⅔ of one core free (32% idle, load 3.3). That is not enough for 1win's SPA to paint promptly — under it a match tab reached the DOM but was still showing 1win's loading skeleton after 150s, so the No-Goal cell was never clickable.
Practical guidance: PREWARM_MAX_TABS=6 wants roughly 8 vCPU / 8GB. On a 2-vCPU
box set it to 1, stop the approval-viewer browser when it is not in use, or run
auto-bet on its own instance.
Watch for leaked Chromium processes too. A short-lived script that imports
onewin-auth.mjs starts a browser pool as an import side effect and the pool
keeps the event loop alive, so the script never exits; orphaned pools were
observed accumulating to 49 Chromium processes holding 3.4GB, which pushed load to
9.9 and made the host look far more saturated than it is. Mirror resolution was
moved to onewin-mirrors.mjs (plain fetch, no side effects) so diagnostics no
longer trigger this.
Verifying it without betting
node scripts/diagnostics/test-prewarm-tab.mjs # 1 random live match
node scripts/diagnostics/test-prewarm-tab.mjs 3 # 3 random live matches
node scripts/diagnostics/test-prewarm-tab.mjs <url> # a specific match pageThis runs the real production pre-warm path against live matches picked from
1win's own live list, types a fixed stake of 2, and never clicks Place a
bet — no wager can result. Step screenshots and a JSON report land in
DATA_DIR/test-screenshots/prewarm. The same rehearsal is available in the app
through POST /api/autobet/manual-prewarm-test.
To enable real betting, sign in to 1win in the app, verify the wallet and currency, choose a stake rule (Fixed or All in, which uses 1win's own "All in" control), then turn on Auto-Bet.
Repository layout
src/backend/— Node backend runtime modules and entry point. The auto-bet chain is four focused modules:cloak-launcher.mjs(stealth Chromium launch, headless→Xvfb escalation),nogoal-betslip.mjs(every 1win DOM interaction),prewarm-tabs.mjs(the one window and its tabs),autobet-engine.mjs(config, state, placement).server.jsonly adapts the monitor to the engine; it holds no auto-bet rules of its own.src/web/� canonical browser assets and country flags.app-shell/� retained separately because its native-shell JS/CSS diverges from the canonical web client.tests/unit/,tests/integration/,tests/live/� isolated test tiers.scripts/� build, deploy, maintenance, diagnostics, and preserved legacy helpers.infra/� canonical Docker/Compose and Caddy configuration. Root wrapper files remain for external compatibility.apps/native/� Tauri/Rust desktop and Android application.local-artifacts/� ignored local screenshots, diffs, and logs.
