Repository navigation
Releases
Releases · coste360/porto
Porto v1.0.0
Added
Public release preparation (ADR 0018, docs/release.md):
- Licence decision recorded in ADR 0018 (Business Source License 1.1, Additional Use Grant for Marina Facilities,
AGPL-3.0 or later three years after each release, brand excluded);CONTRIBUTING.mdrewritten for outside
contributions (no CLA or DCO required for now, open decision D15);SECURITY.md(security@coste360.it,
supported versions). - Release workflow
.github/workflows/release.yml: on avX.Y.Ztag it checks the tag and the changelog section,
publishes the multi-arch imageghcr.io/coste360/porto(SBOM, provenance,BUSL-1.1label) and the GitHub
Release with the notes andapi/openapi.yaml; onlyGITHUB_TOKEN. Procedure indocs/release.md. - Deployment templates:
deploy/coolify(SQLite and PostgreSQL compose),deploy/caprover(captain-definition,
one-click app) anddeploy/demo(public demo instance with a nightly reset script). - Desktop: the version check reads the shared Coste360 updates service
(https://updates.coste360.it/porto/latest.json, its manifest format with the extra top-levelurl; unknown
fields ignored,release_notesas fallback link) with the User-Agentporto/<version> (desktop; <goos>/<goarch>)
(m6a-plan D16 closed). - Packaging: macOS notarisation waits at most
MACOS_NOTARY_TIMEOUT(default 45m) and fails with the submission id
when still pending, or with Apple's log when rejected; the Windows uninstaller and installer are signed by Inno
Setup with the sameWINDOWS_SIGN_CMDhook (scripts/desktop/inno-sign.sh); the Linux AppImage falls back to the
browser build whenever the window build cannot start (missing WebKitGTK 4.1, older glibc), checked in CI. - Demo:
porto seed demo --reset(only withPORTO_DEMO_INSTANCE=true, refused unless the database holds only the
demo organization, recognised by thedemo-organizationmarker in the data directory so that visitors renaming
it or adding marinas do not block the reset) wipes the database and the stored files and seeds again, used by
deploy/demo/reset.shfor the nightly reset of a public demo (docs/demo.md). The seed now prints the counts of yard, lifting, rental,
online booking and sensor data and the seed time, and adds online bookings in every state, the seasonal renewal
history (dashboard "Tasso di rinnovo"), a fuller yard (quotes in every state, work orders across the board with
hours, materials and external costs, planner assignments, lifting schedule), rental days with handovers, and
one open sensor alert of every kind. - UI polish before the first release: every navigation item has an icon (collapsed sidebar shows icons and the
avatar only); the rental planner day header shows month labels only where they fit, hour bookings are drawn
above the margins with their code fitted to the bar, half-day columns show the band label; the berth calendar
stretches the 14- and 31-night views to the width; the shed planner accessibility lens colours every boat and
shows a key; the operational map and the phone map fill the window height.
M12 "Sensori e scheda pubblica" (spec 24, 25, 9.2, 10.6, 12.5, 23, 27, 30, m12-plan, ADR 0017):
- Sensor devices (berth, segment
[from_cm, to_cm)of a linear area, or pedestal socket) with model, firmware, map
position, battery, signal and last contact; gateways and polled sources (ext.SensorSource, fake source in dev,
demo and tests only); sensor settings per marina (harbor.configure, If-Match). - Device and gateway keys (
po_sns_…,po_gw_…) fromcrypto/rand, shown once, stored as a SHA-256
hash plus an encrypted HMAC verifier, rotation with a grace period, revoke; a restore under another
PORTO_SECRET_KEYneeds re-keying (INV-105). - Ingestion
POST /api/ingest/v1/readings: batches up to 500 readings / 256 KiB signed withPorto-Keyand
Porto-Signature(HMAC-SHA256, ±300 s) or a gateway bearer key over HTTPS; the marina comes from the key, never
the payload (INV-102); idempotent on(device_id, observed_at)(INV-101); per-item results; rate limits per
device key, gateway key and IP prefix. Protocol for vendors indocs/sensor-protocol.md. - Pure hysteresis (occupied/free/unknown after N agreeing readings or T minutes, default 2 or 5 min), silence to
unknown, reset on move; physical state changes insensor_state_changeswithsensor.state_changedin the
same transaction. Raw readings keptPORTO_SENSOR_RAW_RETENTION(default 30 days) and excluded from backup. - Reconciliation (pure rules R1–R8, job
sensors.reconcilewoken by ingestion): unexpected occupied, transit
arrival proposal, annual vessel away (automatic presence out only when the marina enables it), release proposal
whererelease_policy = with_consent, return detected, early return over a resold berth, silent or low-battery
sensor proposing a maintenance task. Alerts are proposals: contracts and occupancy change only on an operator
confirmation through the existing services (INV-100, INV-104). - Sensori map context (lens, legend, markers, quick card) updated live by SSE
sensor.changedwithout personal
data; alerts inbox and the dock "Avvisi" tab on the phone; per-berth and per-device physical history (physical
night = majority of 22:00–06:00); reportsensors(discrepancies) with CSV/Excel export; day-view anomaly
sensor_discrepancy. - Marina profile edited in the panel (position WGS84, contacts, VHF, hours, features, depths, services, photos
re-encoded without metadata, it/en) and published on/p/{slug}next to the booking page: simplified map without
codes or occupancy, transit price matrix of the list flaggedpublish_matrix, aggregate free berths per category
when enabled (never which berths or vessels), booking button, JSON-LDMarina/Place, canonical and hreflang;
public APIs cacheable, rate limited and cookie-free (INV-103). Nothing is sent to external directories. - API: tags
sensors,sensors-ingest,profile,profile-public; eventssensor.state_changed,
sensor.anomaly,sensor.changed,profile.changed; jobssensors.poll,sensors.silence,
sensors.retention,sensors.reconcile. Migrations00026_sensors,00027_marina_profile(SQLite and
PostgreSQL). - Demo seed: a gateway, the fake source, 40 berth devices on the largest pier, 2 segment devices and 1 pedestal
device with a back-dated history and 6 h of readings, an alert of every kind, one automatic presence, one silent
device and one at 12 % battery; a published demo profile with photos, POIs, aggregate occupancy and the transit
price matrix (the demo price list now setspublish_matrix). - Load acceptance: 1,000 simulated sensors with one reading per minute each on SQLite stay well within 2 s from
accepted reading to SSE and to a fresh operational view (TestAcceptance_M12_SensorLoad). - Not in M12: a reference hardware partner (open decision), MQTT/LoRaWAN code in the core, automatic release
without confirmation.
M11 "Noleggio" (spec 19, 20, 21, 29.2, m11-plan, ADR 0016):
- Rental fleet: units linked to registry vessels (category, seats, licence requirement never/always/threshold on
engine kW, fuel, deposit, status, home berth, own or third-party owner), bookable skippers with weekly hours,
days off and licence, formulas (hourly, half day on configurable bands, day, multi-day, week; with or without
skipper; fuel included or metered), extras and marina rental settings (open window, bands, buffer, hold TTL,
checklist, grace and late-fee step). - Pricing through the pure engine (
pricing.RentalQuote,EngineVersionunchanged): rental items of the price
lists with time unitshour,half_day,day,week, scopetime_bands, rate periods per priced date,
customer category and channel; skipper line and extras per rental or per day; stateless quote listing every
valid formula; reproducible snapshot on the booking (INV-20,RentalEngineVersion). - Bookings (
NL<year>-NNNNNN) on 30-minute slots: no double booking of a unit or a skipper enforced by the primary
keys ofrental_slotsandrental_skipper_slots, with a configurable buffer row after each rental (INV-18,
INV-19, ADR 0003); holds ("Opzione") with lazy reclaim of expired holds at conflict time and the
rental.holds_expirejob (INV-05); blocks; states held → confirmed → checked_out → returned → closed, plus
cancelled and no_show, only throughrental/reservationwith audit and outbox (INV-97). - Planner unit × time (hours, half days, days) on a shared SVG grid: drag to create and to move, colours by state,
409 highlight on overlap; bookings list and booking page with the handover panel. - Check-out and check-in from the phone (Today, New walk-in booking from now, Scan, Till): renter identity and licence typed by the operator and
encrypted, document photos, rental contract signed on screen (newscreensignature driver; OTP and paper
unchanged), deposit in cash, card POS, pre-authorisation through the payment driver or waived, checklist, fuel,
engine hours and photos (offline kindrental.checklist); on return the actual time with the late fee of the
formula, metered fuel, extras, damages with photos and estimate, deposit released or retained fully or partially.
A deposit is never revenue until retained (INV-98); one main and one return charge per booking (INV-99). - Quick cash in one step (cash, card POS, payment link) with the commercial document through
FiscalDriver;
invoice-requested charges go to the export. The payment link and the deposit pre-authorisation page are shown as
a QR for the renter's own phone; the payment link needs a registered customer (disabled for walk-ins). - An unexported, unpaid main charge is cancelled and re-created (link
replaced) on a reprice, a change of
customer or ofinvoice_requested, and cancelled with the booking; exported or paid charges get linked
adjustments, and ...