Changelog
Cosa c'è di nuovo.
Ogni miglioramento alla piattaforma, come si spedisce.
· kamo-marketing
Fornire un CSP, COOP e tipi fidati
Aggiunge i tre intestazioni che il Trust and Safety audit ha chiesto, più Referrer-Policy e autorizzazioni-Policy. Ogni origine della politica è stata presa da che cosa il sito effettivamente carica piuttosto che indovinato — il pacchetto widget di chat su tema.kamocrm.com (che apre un WebSocket ai media. e chiama Api. e capcha.), lo script di analisi sull'analisi., la scala HLS dell'eroe, e la Il punto finale della sfida. Audited la pagina costruita contro il criterio finito risorsa per risorsa: zero bloccato. ON script-src 'unsafe-inline' — questo è un vero compromesso e vale la pena di affermare piuttosto che seppellire. La forma forte di un CSP è un nonzio per risposta con 'strict-dinamica', e non è disponibile qui: un nonce significa intestazioni di lettura() durante il rendering, che opta per il percorso di generazione statica. Questa applicazione ha ~200 percorsi prerenderati e il loro essere statico è portante di carico — un percorso dinamico con nessun limite di carico non prefetches nulla, così ogni clic di collegamento inizia freddo. Ecco. non è una fuga basata sull'hash, perché gli script in linea sono RSC di Next dati di volo, i cui contenuti differiscono per pagina e per costruzione. Quindi: questo CSP non ferma uno script inline iniettato. Ciò che fa è limitarsi ogni script esterno, fetch, frame, immagine e media carico a origini note, forbid plugins, pin the base URI così iniettato markup non può ri-root relativo URLs, e interrompere i messaggi dei moduli a terze parti. TYPES TRUSTED ha preso tre tentativi per rendere sicuro, e i fallimenti sono i parte interessante: 1. Una semplice inline <script> prima nella <head> del layout non è la prima. Reazione paranchi Il prossimo ` пscript async src>` tags sopra qualsiasi cosa il layout rende, così la politica è atterrata dopo dodici di loro — e gli script asinc eseguire nel momento in cui terminano il download. Il runtime di Turbopack è tra loro e lo fa `createElement("script")` quindi imposta `.src`, il lavandino esatto che lancia senza una politica. 2. `next/script` con strategia="primaInteractive" non lo ha spostato nemmeno; ancora dodici script avanti. 3. Ciò che rende sicuro è che nessun lavandino può sparare così presto. Tutti i 78 dati di volo le spinte sono nel corpo — prima a offset 193,072, con </head> a 11,940 — così React non può iniziare ad idratare fino a quando non è installato la politica, e non c'è un codice che tocca il lavandino in <head> prima di esso. Verificato sul costruito HTML piuttosto che assunto. Due cose si sarebbero rotte senza essere ingordate. Il widget *** è un Svelte costruisce e crea una politica chiamata `svelte-trusted-html`; un nome politico è un stringa runtime, quindi niente su di esso superfici in tipi, lint o la costruzione, e ometterlo dalla lista dei permessi avrebbe gettato e preso il contatto e demo si abbassa. E hls.js e il ***** entrambi costruiscono Web Workers da creareObjectURL, quindi la politica deve passare `blob:` prima del suo test di origine — a blob: l'URL non ha un'utile .origin e altrimenti avrebbe fallito, prendendo l'eroe bobina con esso. Il policy's createScriptURL è controllato contro kamocrm.com, che è rigorosamente più permissivo di script-src, quindi non può rifiutare qualcosa il CSP permette. creareHTML passa attraverso e NON igienica: un vero e proprio igienico-sanitario devono spedire sul percorso critico e potrebbe in silenzio schiacciare i blocchi JSON-LD e il markup del widget. Quella meta' e' un punto debole e un gancio per l'audit, non e' un problema. filtro — ed è dove indurire dopo. Cross-Origin-Resource-Policy è stato scritto e poi rimosso. Applicato attraverso `/:path*` copre le immagini Open Graph, che esistono specificamente per essere embedded da altre origini. frame-ancestors è 'self' per abbinare gli X-Frame-Options: SAMEORIGIN già essendo servito, piuttosto che stringere a 'none' e rompendo un incorporato nessuno ricordato. Verificato contro il vero server standalone, non solo la configurazione: intestazioni presenti su entrambi un percorso prerendered e un percorso ***, e il bene di lunga durata Cache-Control ancora intatto.
· kamo-internal
Codici di monitoraggio al posto del titolare del rinvio
/marketing/discounts era una stub ComingSoon; è ora /marketing/tracking, un spazio di lavoro, con il vecchio percorso mantenuto come un cliente reindirizzare così esistente i segnalibri sono ancora a terra. La scheda legge "Codici di tracciamento". Un membro minge un codice e la pagina mostra loro il link che produce, costruito da l'host effettivamente servire la pagina — interna.acme.com rese E' corretto per ogni cosa. dominio white-label senza che un apex sia hardcoded ovunque. La griglia porta la sei totali per codice, e una riga apre un cassetto con l'imbuto di conversione, il durano trenta giorni, luoghi, dispositivi e referrers, e i visitatori se stessi; un visitatore apre il proprio pannello con origine, rete, cliente, UTM attribuzione e una linea temporale di ogni pagina che hanno aperto. Uso delle bandiere nazionali come altrove. Scope segue i diritti: "La mia squadra" appare per diritto cross-member, "Tutto membri" solo per VIEW ALL MEMBER TRACKING, e il selettore segue l'ambito il SERVER dice che è applicato piuttosto che quello richiesto, quindi un downgrade non può lasciare il membro leggendo le proprie righe sotto un'etichetta "Tutti i membri". Vernici cromati da --colore-primario e --colore-secondario in tutto, da ogni org è white-label e il pulsante predefinito di MUI renderebbe lo stesso blu per tutti loro. Due cose che la griglia aveva bisogno non sono ovvie. I clic di riga vengono filtrati su un attributo di marcatore esplicito piuttosto che stopPropagation, perché AG Grid ascolta nativamente sul contenitore di fila mentre React delegati alla radice — il React handler funziona secondo, quindi il pulsante copia e il menu di riga avrebbero entrambi aperto il cassetto. E la ricerca è solo lato server: un secondo filtro rapido lato client potrebbe solo sottrarsi a un corretto set di risultati, nascondendo righe che corrispondono al membro nome in un ambito senza colonna dei membri. VIEW REFERRAL CODES e MANAGE REFERRAL CODES lasciano l'elenco non rinforzato — essi gate real things now — tramite un audit rigenerato piuttosto che una modifica manuale. Il TS gli specchi acquisiscono i due nuovi diritti e il Servizio MARKETINGTipo a cui puntano, che mancava.
· kamo-register
Portare il referral di marketing nel segno
Il sito di marketing tagga un visitatore di riferimento con kmrt vid e kmrt src sul apex condiviso, quindi registra. richiedere il corpo, rientrando a ?mrt= per il codice quando il visitatore arriva il collegamento ancora nella barra degli indirizzi. Corpo, non intestazioni: il /api proxy spoglia Cookie e l'ingresso riscrive X-Forwarded-Host, che è anche il motivo per cui i cookie non sono httpOnly. Valori assenti sono omessi piuttosto che inviati come stringhe vuote, dal momento che la maggior parte delle iscrizioni non hanno. Il lettore di cookie la pagina logout aveva in linea è ora app/lib/cookies.ts e condivisa da entrambi i chiamanti, piuttosto che un secondo parser scritto accanto a esso.
· kamo-marketing
Onora un link partner e riferisci cosa ha portato
Un visitatore che segue https://www. ottiene due cookie di prima parte dal middleware bordo — un GUID opaco che dura due anni e il codice se stesso per novanta giorni — e ogni pagina che si apre dopo viene segnalato una volta, da idle, da sendBeacon. Il dominio è derivato dalle intestazioni inoltrate, non da nextUrl. Prossima costruzione URL del middleware dal suo proprio indirizzo di ascolto, quindi il nome seguenteUrl.host è il container's, e l'attributo Domain silenziosamente non è mai apparso: un cookie host-only appresso al presente registro. Non c'e' niente che non va da nessuna parte. cookie-consent cancella attraverso lo stesso helper, quindi il due non possono allontanarsi di nuovo. Un visitatore che non ha mai seguito un link partner non ha mai dato un cookie e mai Beacon. Questo, piuttosto che un banner toggle, è ciò che mantiene questo proporzionato: il record dietro i totali di un partner è quello che il partner viene pagato contro, e un cancello di consenso non avrebbe reso i numeri più piccoli, avrebbe fatto loro torto — le visite ancora avvengono e le iscrizioni ancora convertono, solo smettere di essere attribuibile. Entrambi i cookie sono comunicati sotto Essential sul Cookie Politica con un controllo che li chiarisce. La nostra analisi dei prodotti rimane gated. /api/mrt è un limite di fiducia, non un tubo: attacca la chiave API di questo sito, quindi convalida il GUID, il codice e l'URL segnalato, ricostruisce il carico utile da valori che possiede, rifiuta un cross-site caller, e inoltra solo l'ingress-set peer IP — la catena X-Forwarded-For in entrata è controllata dal cliente alla sua estremità sinistra e altrimenti lasciare che un chiamante scelga l'IP i registri del rapporto della commissione. Si'. risponde 204 prima dell'inoltro, e avverte al massimo una volta al minuto quando il upstream è irraggiungibile, perché la forma precedente ha trattato un 404 esattamente come successo. Il rendering statico è intatto: nessun cookie() o intestazioni() in qualsiasi pagina, nessun script tag, nessun framer-motion sul faro. check-mrt-cookie.ts pins gli invarianti che non hanno altra guardia — compreso che il faro rimane ungated e che nulla è raccolti da un visitatore non referenziato.
· SecurityService
Pubblica la registrazione e la creazione di org per i visitatori in pista
Il sito del registro ora inoltra la GUID del visitatore di marketing e il codice di tracciamento in l'organismo di richiesta — non un intestazione, perché il proxy spoglia Cookie e l'ingresso riscrive X-Forwarded-Host — e registra() li passa a un segnale di registrazione quando sono presenti. La maggior parte delle iscrizioni non portano né pubblicano nulla. MarketingSignalPublisher serializza con un LOCAL ObjectMapper, deliberatamente non il fagiolo condiviso. Quel fagiolo scrive anche il Redis *** mappa di sessione e 65 chiamata siti leggere ids indietro da esso come numero; dandogli un globale Long-> Regola di stringa trasforma la risoluzione org in un ClassCastException che gli aiutanti ingoiano, e un null org è un 401 su ogni richiesta autenticata su quattro servizi. The editore è il miglior sforzo, detiene una connessione NATS opzionale, e non lancia mai. Entrambi i segnali sono registrati dopoCommettere con il carico di paga istantanee all'interno del transazione, quindi il consumatore non può cercare una riga utente o di organizzazione che ha Non sono ancora stati impegnati. RoleRightsSyncSeedTest ottiene un caso negativo: nessuno dei due nuovi tracciamenti i diritti possono mai entrare NEW RIGHT SEEDS, dal momento che vederli concedere ogni visibilità del titolare del codice di riferimento esistente nell'analisi a livello di visitatori dei colleghi sul boot. I loro id sono fissati a 200 e 201.
· BillingService
Pubblicare la conversione a pagamento per le organizzazioni tracciate
Entrambi i percorsi che timbro BillingSourceType.PAID ora pubblicare marketing.visitor.paid, perché agganciare solo uno silenziosamente manca metà del traffico — AccountSubscription Servizio gestisce l'abbonamento della console, ConsumerCheckoutService il checkout Stripe. Tre cose che hanno ragione di proposito. Pubblica sulla TRASFORMAZIONE, lettura la fonte precedente prima di scrivere e sparare solo quando non era già PAID, quindi un cambiamento di piano o un "piano di cambiamento" ri-POSTed non può gonfiare il contatore. Si istantanee in un record immutabile all'interno della transazione, perché account e targetOrganization sono sia pigri che leggerli da dopo getti di impegno. E la relazione è l'organizzazione TARGET, mai quella del pagatore — un genitore account paga di routine per un abbonamento che si rivolge a un bambino org, e l'imbuto conta l'org che ha convertito, non quello che tiene la carta. Registrato attraverso il sito web. inline, che ottiene anche recordPaidFromCheckout a destra: il suo @Transactional unisce la transazione esterna di Stripe webhook, quindi solo un dopoCommettere un gancio incendi al vero confine.
· APIService
Avviare il segnale di tracciamento a MediaService
Clones forwardPublicWebinar piuttosto che generico forward(): che metodo stampa ogni URI, mappa dell'intestazione e lunghezza del corpo a stdout senza livello, e un il beacon del visitatore trasporta un IP e un referer. La chiave API qui è un tasso-limite e controllo degli abusi, non un limite di tenancy. Uno marketing pod serve ogni host www.* con una chiave pubblica, quindi la chiave è sempre la piattaforma org — il codice di tracciamento propria organizzazioneId è quello che decide Tenancy, a valle in MediaService. Questo ragionamento è in un commento alla chiamata sito in modo da non ottenere "fisso" in un controllo org rotto più tardi.
· MediaService
Servire la scheda codici di tracciamento e ingerire il beacon del visitatore
MediaService già supporta le altre schede di marketing, quindi ci vogliono entrambe le metà di codici di tracciamento: il CRUD autenticato e stats superficie a /api/media/marketing/tracking, e il lavoratore ingerente pubblico dietro APIService bordo. Il maniglione ingestito non fa altro che convalidare e incidere. Tutto reale — il ricerca di codice, il geo lookup, l'utente-agent parse, le quattro scritture — accade su un esecutore limitato dal thread richiesta, e la risposta è un 202 con un vuoto corpo indipendentemente dal risultato, perché una pagina di marketing non deve mai aspettare sull'analisi e un pubblico chiamante non deve mai imparare che cosa è andato storto. La politica di rifiuto è AbortPolicy, non CallerRunsPolicy: caller-runs spingerebbe il lavoro del database sul thread HTTP, che è la cosa esatta che questa forma esiste per prevenire. A la coda piena cade il colpo e lo conta. Ogni stringa fornita dal client viene clipped sulla larghezza della colonna prima che sia persistito. Hibernate non applica @Column(lunghezza) a runtime, quindi su un pubblico endpoint a overlength Referer or Accept-Language è SQLSTATE 22001 e prende l'intero faro giù con esso — trivialmente attivabile da chiunque. Una visita è nuova quando la riga di collegamento è stata vista più di 30 minuti fa, corrispondente la finestra di installazione di analisi in modo che i due non disapprovano mai "visite". Bots sono calpestati, caduti, e contati contro nulla. Scope è risolto lato server e ri-validated: "managed" esigenze cross-member diritto e include sempre il chiamante, "tutto" ha bisogno VIEW ALL MEMBER TRACKING e legge per organizzazione in modo che nessuna clausola IN possa mai essere ha consegnato una collezione vuota. Una portata non autorizzata si degrada a "proprio" piuttosto che errore, e la risposta riecheggia la portata effettivamente applicata in modo che il cliente può spostare il suo selettore invece di mentire su ciò che il membro sta guardando. Le risposte sono record con ids string e contatori numerici. MediaService non ha filo-livello Long-> Regola di stringa, quindi un Long crudo in una mappa spedirebbe un numero JS-lossy con nulla per catturarlo; TrackingCodeDtoWire Perni di prova entrambe le metà. Il filtro durevole del consumatore di conversione è il marketing.visitor.* ed è fisso per sempre — un durevole mantiene il filtro che è stato creato con, e l'ampliamento più tardi produce un SUB-90011 permanente su un consumatore che sembra ancora sano. E' una cosa che non va. contatore solo quando il timbro condizionale ha effettivamente colpito una fila. Una notte riconciliare ricompute tutti e sei i contatori dalle righe che questo servizio possiede, quindi una caduta incremento si guarisce.
· kamo-shared-library
Codici di tracciamento del modello, i loro visitatori e le visite che portano
Un membro minge un codice, condivide il codice. i sei totali sul Tracking Codes scheda rispondere a quello che quel link effettivamente fatto: visitatori unici, visite, richieste di pagina, iscrizioni, organizzazioni create e pagate conti. Quattro tavoli lo portano. MARKETING TRACKING CODES memorizza la stringa di codice stessa piuttosto che riconfermarlo da membroId e iterazione, quindi il percorso pubblico ingestisce è una sonda unica-indice senza parasing e un malformato ?mrt= è una miss invece di un NumberFormatException. MARKETING VISITORS è la colonna vertebrale dell'identità, una riga per cookie GUID, e il suo codice attribuito è scritto una volta e mai spostato: con l'attribuzione dei tre contatori di conversione non può raddoppiare visitatore che più tardi arriva su un secondo codice. MARKETING VISITOR CODES è il visitatore x collegamento di codice e l'autorità per i contatori del traffico, e MARKETING VISIT EVENTS è la timeline dell'append che il cassetto del visitatore legge. I contatori sono mantenuti, non derivati, perché la griglia ne rende sei per riga e sei COUNT (DISTINCT) aggregati su una tabella di sola appendi su una riga negozio è una scansione per carico pagina. Ciò che rende sicura è che ogni urto è un atomico SET x = x + 1 piuttosto che una lettura-modifica-scrittura, e ogni timbro di conversione è un UPDATE condizionale gated su IS NULL in modo che gli aggiornamenti di messaggi NATS ricondizionato zero righe — il filecount è il dedupe. Entrambi i test "prima volta visti" sono nativo INSERT ... SU CONFLICT NON ESSERE per stesso motivo. L'ovvia alternativa, cattura la violazione unica e rileggere, non può funzionare qui: su YugabyteDB la violazione interrompe tutto transazione (25P02) e la primavera lo segna solo rollback, quindi una gara persa sarebbe caduta l'intero faro — la fila dell'evento, la fila del visitatore e ogni contatore con esso. Diritti: 154/155 mantenere i loro nomi costanti e sono solo rilabelled a "Visualizza" e "Codici di monitoraggio di gestione", perché i diritti viaggiano per nome nel token di sessione e un rinominarli le striscerebbe da ogni sessione live senza errori di compilazione. Ids 200 e 201 sono nuovi e gate cross-member visibilità. Né è seme in NEW RIGHT SEEDS: prima di questa funzione nessuno poteva vedere il visitatore di un altro membro analytics a tutti, così seeding da VIEW REFERRAL CODES consegnerebbe ogni esistente che non hanno mai avuto.
· KlusterServices
MARKETING EVENTI per segnali di conversione dei visitatori
Codici di monitoraggio del marketing accreditano un partner per le iscrizioni, organizzazioni e i conti pagati il loro link prodotto, e quei tre momenti avvengono nei servizi che non possiedono le tabelle di tracciamento: SecurityService pubblica marketing.visitor.signup e marketing.visitor.org-created, Billing Servizio pubblica marketing.visitor.paid e MediaService consuma tutti e tre. Non c'è nulla che crei questo flusso di applicazione — i produttori pubblicano solo — così deve esistere qui prima di distribuire, o le prime conversioni sono pubblicate in nulla e i contatori silenziosamente rimangono a zero. R1 come ogni altro flusso su questo server single-node.
· kamo-internal
Ridurre l'accendino a vuoto
La colonna Descrizione mostra {No Descrizione} al posto di "Nessuna descrizione fornito.". La stringa era inglese hardcoded, quindi ora viene da dizionario — con le bretelle applicate nel componente, da un messaggio portarli sarebbe parso come un segnaposto dell'ICU.
· kamo-internal
Riordinare e ritirare le schede del documento
La striscia ora legge gli originali | Modifica (n) | Modificati | Binders | Common | Carica, e comuni commerci l'icona ingranaggio per una libreria uno ora che non più a lungo dice "Sistema". Ogni scheda indica un "valore" esplicito quindi l'ordine visivo è libero di cambiare senza rinumerare i controlli attiviTab che il resto il file è costruito su. Condividi con me, storia, documenti nascosti e cestino rilasciano le loro etichette per tooltips — La storia si allontana dalla striscia principale per unirsi a loro, che anche fissa il suo effetto fetch: è stato gated sulla scheda 5 (Uploads) mentre il pannello rende su 6, così la storia elenca mai caricato.
· kamo-internal
Ricostruire la base di conoscenza come console
I quattro schermi della base di conoscenza sono stati l'ultimo nell'app ancora indossando il vecchio Guarda, e tre di loro hanno dichiarato cose che non erano vere. /kb ha mostrato "Pinned Articles" da un endpoint che ha inviato righe pin con il articolo spogliato da @JsonIgnore — la sezione non è mai apparso per Chiunque. Sotto di esso, "Recent Articles" è stato il primo dieci nodi dell'articolo albero, in ordine albero, ciascuno timbrato PUBBLICATO con una data vuota: né recente né necessariamente pubblicato. L'albero accanto a loro è crollato su ogni navigazione, perché ogni ramo ha mantenuto il proprio usoState. Un'idea porta la ricostruzione: IL RAIL è IL FILTER. Il conteggio di una tegola e il lista si apre entrambi provengono da partiteFilter nel modello, quindi una lettura piastrelle 4 apre esattamente quelle 4. Non c'è una seconda fila di chip che ribadisce la stessa predica in parole diverse, in attesa di disaccordo. Intorno a esso: ricerca che match titolo, indirizzo e corpo e segni che ha colpito (la vecchia scatola debounced 500ms chiedere a un server che ha cercato il contenutoPlainText solo, quindi digitando il titolo esatto di un articolo non ha restituito nulla), un albero che ricorda quello che si aperto e può essere ridimensionato, faccia su ogni scheda dalla directory membro, e a Needs Attenzione pannello — recensioni che hanno aspettato, bozze andato tranquillo, pubblicato articoli che nessuno ha toccato in un quarto. /kb/resources è stato un dialogo modale reso piatto su un percorso, con open={true} e un onClose che ha navigato via. È una biblioteca ora: una rotaia che pesa quello che l'organizzazione sta memorizzando e raddoppia come il filtro tipo, piastrelle con facce reali per video e file piuttosto che una griglia di scatole grigie identiche, drop-anywhere upload with real per-file progress (fetch non può segnalare progresso, quindi un video 40MB sembrava esattamente un'icona 4KB), e rinominando posto invece di un modale lanciato da un piè di pagina. L'inserto-media dell'editor picker è ora le stesse piastrelle, lo stesso pannello di dettaglio e lo stesso usoKbMedia gancio in una finestra di dialogo — un kit, due superfici, in modo che non possono derivare. /kb/reviews era una lista di titoli. Un recensore poteva vedere che quattro cose erano aspettare e nient'altro — non chi li ha scritti, non quanto tempo si erano seduti là, non quello che hanno detto — e decidere significa aprire ogni articolo, scorrere a un pannello in basso, e tornare indietro. Peggio di tutti un fetch fallito reso come "Nessun articolo in attesa di revisione": la coda si è rivelata chiara su una giorno potrebbe non essere stato. Ora la coda porta l'età di ogni articolo, e dimensione; l'articolo siede accanto a esso; e i pulsanti verdetto sono accanto a quello si applicano a. /kb/new era una forma che non ha detto nulla a un autore. Il campo slug ha detto "Auto-generato dal titolo" e quindi consente di salvare un slug il server rifiuta con un 400 dopo l'articolo è scritto. Le opzioni rientrate del genitore con due spazi, che l'HTML crolla, così ogni opzione sembrava di alto livello. E tutte e tre le caselle di visibilità potrebbero essere spuntate con l'allenamento AI lasciato fuori, che silenziosamente significa che nessuno lo vede da nessuna parte. Il compositore risponde a questi tre domande prima del lavoro piuttosto che dopo il rifiuto, e offre Save progetto accanto Crea e invia. Fissati lungo la strada, tutti loro visibili: - KbStatusBadge ha spedito quattro parole in inglese hardcoded e quattro hex hardcoded coppie — una console spagnola ha detto "Draft" in una tavolozza grigio no white-label potrebbe muoversi. - KbPinButton catturato èPinned una volta, quindi una pagina che impara il suo stato pin dopo il primo render (ogni pagina qui) ha mostrato un perno sagomato sopra un pinned articolo per sempre; una richiesta fallita ha lasciato il set di ricerca pin. - L'articolo legge l'articolo. incriminato, un campo KbArticolo non ha. - mediaId è stato dichiarato numero attraverso i quattro tipi di nodo editor e estrarreMediaIdsFromJson, mentre KB MEDIA.UID è un UUID e i valori hanno sono sempre state stringhe. È sopravvissuta solo perché nulla ha mai fatto aritmetica su uno; un singolo Numero(mediaId) sarebbe stato un NaN silenzioso. - KbMediaResource.uid aveva la stessa bugia. - Le richieste dei media hanno corso: una risposta di ricerca anticipata lenta potrebbe atterrare dopo un veloce più tardi uno e riverniciare la griglia per una query nessuno stava guardando. Le regole pure vivono in app/lib/kbConsole.ts e app/lib/kbMedia.ts con 52 test, perché vitest qui raccoglie solo .test.ts — una regola scritta all'interno di una .tsx è una regola senza test. Che cosa codificano in tutto: un sconosciuto non è mai a zero. Una data mancante dura piuttosto che diventare l'epoca, un le vernici di stato non riconosciute neutre piuttosto che verde, una coda vuota non ha elemento più vecchio piuttosto che uno zero-day e una risorsa il cui uso conta mai arrivato avverte invece di promettere che è inutilizzato.
· SecurityService
Enforce is fake, e chiudi i buchi che lasciano un segnale incustodito in
Due metà di un incidente. Un account registrato, verificato un indirizzo a un usa e getta provider, ha preso una sessione di auto-login e ha creato cinque organizzazioni in tre minuti e 40-due secondi — quattro di loro byte-identical "Acme Corp" righe che differiscono solo nella loro dominio. Ha prodotto righe ZERO in system access logs, quindi non c'è IP e nessun utente-agent per qualsiasi cosa. - Per forzare la bandiera... - Utente Servizio: `u.is fake = FALSE` va in ACCOUNT QUERY PREFIX, non in un chiamante. Tutti e tre gli identificatori di login — nome utente, e-mail di account, e la casella di posta primaria re-lookup keyed by id — riutilizzare quella proiezione, quindi una clausola chiude tutti e tre e una quarto identificatore aggiunto in seguito lo eredita. Un account contrassegnato legge come inesistente e ottiene il generico "utente non esiste o password è errato"; un messaggio che nomina bandiera direbbe a un abusatore esattamente cosa cambiare. - KSessionService.createSession: rifiuta il diritto. Questa è la vita più stretta nella sistema per "può questo atto conto" — ogni *** è coniato qui, così un cancello copre password login, enter-as, desktop SSO, dispositivo auth E l'imbuto del registro post-verificazione auto-login, che è come l'account in questione ha ottenuto una sessione senza mai chiamare /login. Lancia piuttosto che tornare nullo, perché trenta chiamanti assumere un id non nullo e sarebbe NPE da qualche altra parte completamente; login e l'autosession percorso catturarlo e modellare una risposta pulita. - FakeAccountGuard: due livelli di proposito. Il controllo del conto unico è UNCACHED (è il backup i cancelli duri, dove una risposta stante significa che un account contrassegnato continua a entrare); il id imposta sono memorizzati in cache per 60s (retro filtro di lettura, dove il caso peggiore è un org sintetico in un elenco per meno di un minuto). Falli aperti — nascondere una manciata di righe non è valore 500ing un elenco org — e NON memorizza un guasto, quindi un blip non diventa un minuto della bandiera silenziosamente non funziona. - OrgObjectStorageSweep: smette di istantanee org sintetici. Qui c'era il denaro: le istantanee sono scritte per org al giorno per sempre, e cinque org abbandonati avevano già diventare il ~9% di quel tavolo. Un account che non può accedere non fa nulla sul lavoro piattaforma continua a fare per suo conto. - FakeAccountController: flag, unflag, list — dietro MANAGE ORGANIZATIONS piuttosto che un nuova piattaforma destra, perché la piattaforma di kamo-internalRightCoverage test afferma il diritto contro le superfici rese della console. Revokes live sessioni su flagging, dal i cancelli sono su MINTING una sessione, non su utilizzando uno; rifiuta di contrassegnare l'Utente del Sistema, che renderebbe la piattaforma incapace di entrare qualsiasi bambino org. - I buchi... - /Registrati non legga mai '***Token`. Il registro UI ha sempre risolto un Capcha sfidare e postato il risultato; grep per il campo in tutta Java non ha restituito nulla. Il widget era nel browser e il endpoint era aperto. Ora verificato, e FAIL CLOSED — asimmetrico con login di proposito: il login può verificare solo un carico di pagamento quando uno è presente, perché il client mobile non invia nessuno e richiederlo ci si blocca utenti reali fuori. La registrazione ha esattamente un cliente e già invia il token. - CapchaVerificationService ha restituito TRUE quando `capcha.service-url` era unset, quindi una configurazione l'omissione era indistinguibile da una sfida risolta. Ora rifiuta, dietro `capcha.allow-unconfigured` (default false) per le piste locali. Nessun cambiamento di produzione — il la proprietà è impostata in kamowssecurity-config — ma il bypass latente è andato. - POST /org non aveva limiti di tasso di alcun tipo. Ora tappato per ACCOUNT (non per IP) una chiave IP sarebbe punire tutti dietro un NAT aziendale). Deliberatamente generoso: un vero cliente creato tre org in cinque e mezzo minuti, due con lo stesso nome e alias, mentre Sto lavorando al mago. Un limite stretto li avrebbe bloccati. Conta i tentativi, non successi, e non si apre su un outage Redis. - L'org alias non ha avuto alcun controllo di unicità su questo percorso — solo il dominio ha fatto, ecco perché quattro live orgs condividono "acme-corp" e perché il creatore ha variato solo il dominio ogni volta: era l'unico campo che il server avrebbe rifiutato. Ora un 409, indirizzato al fornitore di sicurezza perché `(security provider id, alias)` è la coppia di login risolve un host web-alias su. - REGISTRAZIONE / REGISTRAZIONE REGETTO / EMAIL VERIFIED / ORG CREATED sono ora scritti con IP e user-agent. Ogni regola di rilevamento chiavi fuori eventi di login, quindi l'intero registro imbuto seduto al di fuori del campo visivo del motore di rilevamento. Decisamente NON fatto, avendo controllato: il login non è fatto ***-strict (rompe il mobile app, ha bisogno di un rilascio coordinato); i domini di posta elettronica monouso non sono bloccati (una vera segnaletica del cliente da proton.me e una regola ingenua "non un fornitore mainstream" blocca reale persone); nessun blocco auto-contenuto (il segnale comportamentale più forte — nomi org duplicati creati minuti separati — corrisponde a un cliente reale esattamente). Schema prerequisito già soddisfatto: KamoInitializer applicato users.is fake prima spinta condivisa-lib, verificata come `boolean NON NULL DEFAULT false` con il suo indice parziale, e re-ran idempotently senza schiarire la bandiera.
· KBService
Servire ciò che la console di conoscenza legge, e risolvere ciò che non potrebbe mai
Quattro endpoints le esigenze della base di conoscenza ricostruita, e tre fatti l'API era incapace di dichiarare. GET /api/kb/pins ha restituito una pagina di pin ROWS, e KbArticoloPin.article è @JsonIgnore — così la risposta ha portato un pin id, un membro id e una data, e nulla di cui l'articolo era stato pinned. La home page ha chiesto che lista dal momento che ha spedito e non ha reso nulla, perché una busta di pagina non ha .lunghezza e la guardia di fronte alla sezione era un controllo di lunghezza. Pinning ha non ha mai lavorato fine alla fine. Ora restituisce gli articoli stessi, risolto all'interno di una transazione piuttosto che appoggiarsi alla visione aperta. GET /api/kb/articles/overview è nuovo: lo stato conta, il reale di ogni articolo date, autorevolezza, conteggio delle parole ed espulsione, più le coperture del chiamante, in una letta. La home page usato per falsificare "recent" prendendo i primi dieci nodi di l'albero e stamparli PUBBLICATI con una data vuota. GET /api/kb/reviews/queue è nuovo: cosa aspetta e cosa è stato di recente in una sola richiesta. Un recensore che guarda una coda vuota chiede la stessa domanda in entrambi i modi — non è niente di attesa, o ho appena chiarito. La sua lettura decisa è un LIST con un limite, non una Pagina: all'interno della sola lettura transazione una pagina discarded COUNT non è mai affermazione #1, quindi Yugabyte non può riavviarlo e una scrittura concomitante trasforma la coda in un 40001. GET /api/kb/media invia ora l'articolo di ogni rigaPaese e, per i tipi una griglia estrae, la sua URL prescritta. Il conte è stato popolato solo da endpoint a singola risorsa, quindi la libreria è "utilizzata negli articoli N" avvertimento prima un'eliminazione non potrebbe mai sparare — un diagramma incorporato in nove articoli eliminati come tranquillamente come un orfano. Gli URL erano una richiesta per thumbnail dopo l'elenco era già atterrato. POST /api/kb/media/urls firma un lotto per i chiamanti che tenere le file già. Ogni membro int64 lascia come STRING. Sono 19 cifre unico rowid() valori e un numero JSON è un doppio IEEE nel browser — il modulo di presentazione sulla coda di revisione è arrivato pre-rotondato, motivo per cui non potrebbe mai essere abbinato alla directory dei membri e ad un id crudo seduto dove appartiene un nome. Nessun cambiamento condiviso-librario, quindi nessun KamoInitializer correre e nessuna flotta redige.
· InitializerService
Aggiungi utenti.is fake, nell'unico ordine di dichiarazione una tabella popolata permette
Specchi il nuovo campo `User.isFake`. Quattro dichiarazioni, e l'ordine è tutto migrazione: YugabyteDB rifiuta `ADD COLUMN ... NON NULL` su un tavolo popolato — rifiutando l'intera dichiarazione, non solo la clausola offesa — quindi la colonna arriva nullable, è ha riempito, guadagna il suo DEFAULT, e solo allora è serrato. Collassare questi in uno ALTER produce una migrazione che registra un avviso e lascia la colonna assente, che è uscita a forma di successo per un no-op totale. `@Order(0)` per la convenzione colonna-add. Nel momento in cui l'entità condivisa-lib mappa `is fake`, ogni secondo corridore che carica un `User` attraverso JPA lo emette nella sua SELECT, quindi una migrazione tardiva non sarebbe mai stata raggiunta — un corridore precedente muore su `column u1 0.is fake non esiste` e prende il primo down. L'indice è parziale (`WHERE is fake = TRUE`). La query calda della guardia è "che ids sono flagged", e su una tabella in cui in modo efficace ogni riga è FALSE un indice completo sulla colonna è peso morto mentre uno parziale detiene una manciata di voci. Il `WHERE is fake IS NULL` è portante, non decorazione: KamoInitializer corre in pieno ogni volta, e un nudo `UPDATE utenti SET is fake = FALSE` silenziosamente un-flag ogni conto che un operatore aveva volutamente nascosto, su ogni corsa. Un test afferma quella clausola è ancora lì. Applicato alla produzione 2026-08-19; tutte le cinque dichiarazioni registrate applicate, BUILD SUCCESS. Colonna verificata come `boolean NON NULL DEFAULT false` con l'indice parziale presente.
· kamo-shared-library
Aggiungi is fake, la bandiera che rende un account invisibile invece di cancellato
Un conto sintetico — un artefatto di prova, un apparecchio di semenza, o un cartello giudicato per essere automatizzato — ora trasporta `users.is fake`, e la piattaforma smette di riconoscerlo. Cancellare un account non è una vera opzione: una riga utente è la radice di sette le tabelle satellitari, ogni Membro che possiede e ogni Organizzazione che ha creato, che in attivare i propri domini, funzionalità, modelli di posta elettronica, ruoli e snapshot di archiviazione. Cancellazione significa fuggire attraverso tutto ciò o lasciare orfani, e distrugge le prove di qualunque cosa abbia fatto il conto. Un booleano è reversibile e costa una colonna. La bandiera vive sull'USER, non sul membro o sull'organizzazione, perché la proprietà è l'unico bordo che raggiunge in modo affidabile i dati derivati: `orgs.user id owner` e `members.user id` entrambi puntano qui, quindi una bandiera risponde "è il creatore di questa riga sintetico" per tutte e tre le spine. La registrazione dell'org invece lascerebbe l'account in grado firmare e creare di più. Quello che la bandiera raggiunge, e perché ognuno è qui piuttosto che nel chiamante: - `UserRepository.findSession` e la proiezione auth: l'account flagged risolve a zero righe, quindi legge come inesistente piuttosto che come bloccato. - `MemberRepository` / `TeamMemberRepo`: esclusi dagli elenchi dei membri di org e da entrambe le ricerche directory, accanto al System User che era già filtrato lì. - No, no. l'enumerazione choke punto nove chiamata i siti condividono. `getOrganizationBy Id` non è deliberatamente filtrato — è indietro scrive ed è come la superficie di amministrazione raggiunge un org flagged per sgonfiarlo. - No, no. un org flagged smette di risuonare dall'ospite, quindi non può continuare ad accettare iscrizioni reali sotto il proprio sottodominio di registro. - filtrato nella query, piuttosto che in quella watcher, perché la spazzata prende un lotto fisso ogni minuto e un dominio nessuno possiede sarebbe altrimenti tenere una di quelle slot per sempre, probing DNS di qualcun altro. Ognuno di questi utilizza un chiaro e alias LEFT JOIN. Un bare `o.owner.isFake` percorso in una clausola WHERE fa Hibernate aggiungere un secondo, implicito INNER unire, che sarebbe silenziosamente cadere ogni riga il cui proprietario è null — e per `findByDomain` che è la risoluzione di login. I ricercatori di cui sopra sono additivi; i ricercatori di identità su UserRepository sono lasciati non filtrato di proposito. La maggior parte di loro sono carica-portante per l'unicità pre-controlla e come per le ricerche, e `USERS.EMAIL`/`ALIAS` sono unici per security-provider — quindi un registrazione duplicato-check che non poteva vedere una riga contrassegnata avrebbe navigato oltre il proprio guardia e morire invece su una violazione di vincolo. Aggiunge anche REGISTRAZIONE, REGISTRATION REJECTED, EMAIL VERIFIED e ORG CREATED a AccessLogEventType. `system access logs` ha tenuto solo login, login fallito e logout, così un account che ha registrato, verificato, ha preso una sessione di auto-login e ha creato cinque organizzazioni prodotte zero righe — nessun evento, nessun IP, nessun utente-agente — e ogni rilevamento regola i tasti fuori eventi di login, lasciando l'intero registro imbuto inosservabile. `users.is fake` esiste già in produzione: KamoInitializer UserIsFakeMigration prima di questa spinta, quindi nessun servizio può costruire contro uno schema non migrato.
· kamo-internal
Ricostruire /hr/attenzione come console payroll
Otto tavole piane su otto pannelli che ognuno disegna la propria voce, tavolo, scheletro, linea di errore e Snackbar — e si era allontanato. La revisione è tre cose a livello di pagina e una ricostruzione di ogni pannello sopra di loro. **Un riepilogo legge** riporta una nuova barra di comando e ogni badge ferroviario. Si'. risponde alla domanda che lo schermo non poteva: se qualcosa si trova tra qui e paga. Quello usato per prendere tre clic — un amministratore ha aperto il roster senza sapere che dodici eccezioni di blocco si rifiutano ogni approvazione che stavano per fare sulla scheda successiva. Blocco e in attesa di chip deep-link al pannello che li possiede. Il polling è recintato visibilità pagina, quindi una scheda sinistra aperta pernottamento smette di chiedere. **Un ospite brindisi**, issato sopra l'interruttore del pannello. Un messaggio sollevato da un azione che naviga — "chiara prima l'eccezione di blocco", che invia il lettore ad un altro subtab — utilizzato per smontare con il proprio pannello prima Qualcuno l'ha letto. **Una mappa del tono** (attenzioneConsole.ts, unit-tested) sostituisce tre che già in disaccordo, e porta la regola che questo modulo si accende: un sconosciuto è mai uno zero. Conto di approvazione, una figura di netto assente e un periodo nessuno ha generato tutti raggiungere l'occhio come "non ci è stato detto", perché rendering uno di loro come 0 dice un amministratore HR una coda che non possono vedere è Via libera. `railBadgeFor` quindi non emette mai un distintivo zero, e `consoleVerdict` è `unknown` prima della prima lettura piuttosto che `clear`. Due lacune chiuse che non erano cosmetiche: Una persona e' una persona. Cinque pannelli stampati a 19 cifre `unique rowid()` sulle righe decidere se pagare qualcuno. "PersonCell" mostra il volto e il nome, con il id sul tooltip; ids rimanere STRINGS fine per terminare e niente parses uno. `useMemberDisplayNames` ora memorizza l'intero directory file piuttosto che il nome da solo, quindi la stessa passeggiata alimenta avatars e titoli di lavoro invece di averne bisogno. Un trapano che non esisteva mai. Il membro scheda Timecard sotto /impostazioni è auto-solo per costruzione — i suoi endpoint non prendono alcun membro — quindi il solo il percorso da "le ore di questa persona sono sbagliate" a "qui è il pugno" era la coda di eccezione, che elenca il giorno e non lo spostamento intorno a esso. `MemberTimecardDrawer` apre l'editor di periodo esistente su un altro membro; tali endpoint prendono un `subjectMemberId` e SecurityService attribuisce il regolazione dell'attore HR, che è ciò che rende il riuso corretto. Per pannello: il roster guadagna ricerca, ordina, una carta e una vista della tabella, una intervallo corrente-periodo e CSV (ogni valore della stringa del server — un file payroll riconcilia contro non deve portare un numero questa app inventato). Approvazioni diventa triage tastiera con carte chiare separate da bloccato quelli e un "approvare tutto pronto" che ancora cammina la macchina statale una carta alla volta e nomina ogni rifiuto. Eccezioni guadagna un tipo istogramma che raddoppia come filtro e, soprattutto, un **Fix it** azione: lo schermo spedito offrendo solo Re-detect e Waive, né di che mette un pugno mancante, quindi il percorso di minor resistenza da "un giorno manca il suo orologio" era quello di rinunciare - precisamente la decisione della coda esiste per far pensare a qualcuno. I periodi di pagamento guadagnano una linea temporale, perché un il divario o una sovrapposizione in un calendario è visibile su una traccia e invisibile in un elenco. L'esportazione calcola la variazione rispetto al periodo precedente invece di consigliare operatore da ricordare per confrontare. Chiude diventa un stepper numerato, dal l'ordine è reale: ricommettere dopo aver approvato i cambiamenti già concordati. Un `ReasonDialog` sostituisce sei laminati a mano che erano alla deriva — alcuni rifinito dopo il controllo, così tre spazi hanno abilitato il pulsante e ha presentato un motivo vuoto che il server ha preso come presente. Sotto Anderson v. Mt. Clemens La ceramica che la ragione è il record, quindi il controllo è sul valore tagliato ora. `TIMECARD SUB TAB IDS` è intatto: rimane il contratto di URL di sola appendice e `ATTENDANCE RAIL` riordina solo le pillole, così ogni segnalibro ancora atterra. Un test afferma che la ferrovia copre ogni id esattamente una volta.
· kamo-internal
Ricostruire la pagina di rete come console operativa
La pagina era l'ultima schermata ancora indossando il vecchio look — un gradiente piatto bar, white card, spinner centrale, alert() per ogni guasto, mentre il resto della console si era spostato nella lingua del pannello strumenti. E anche gettato via dati il backend era già invio: ogni organizzazione propria colore di marca, la sua lista di dominio completo, la sua politica di dispositivo. Un'idea porta la ricostruzione: i numeri sono i filtri. La vecchia pagina aveva una fila di conti e, separatamente, una fila di chip di filtro che ribadisce la stessa in parole diverse — due controlli da tenere in sincronia e due possibilità di non essere d'accordo su cosa significa "di proprietà". Una tegola stat è ora il suo predicato, e il conteggio e la selezione provengono da partiteFilter nel modello. Che cosa ha guadagnato la pagina: ricerca in tutti i sei campi con la partita segnata (un dominio secondario utilizzato per restituire nulla anche mentre la scheda mostrata "+2 più" sotto di esso), / per concentrarsi, quattro tipi, griglia e fitti-list visualizzazioni, pinning, e un cassetto di dettaglio indirizzabile come ?org= le carte diventano più silenziose, dal momento che la fila non prova più a mostrare breve titolo, alias, anno, configurazione, dominio e nome di visualizzazione subito in grigio 0,7rem. Vedi, specie, perni e recenti persistono nelle ui-preferenze del membro, quindi la disposizione li segue tra macchine. L'annullamento è collegato al endpoint che ora esiste, dietro una conferma digitata: ogni altro controllo qui è reversibile dalla persona che lo utilizza e che non lo è. Il file 1341-line è ora solo accordo. Che organizzazione è, e ogni domanda che lo schermo chiede circa uno, vive in reteModel.ts sotto prova unità; ciò che viene chiesto al server vive in ganci/. Le tre viste leggono le stesse derivazioni, quindi non possono derivare in disaccordo su quale le organizzazioni sono raggiungibili. Il movimento è reale ma tranquillo: entrata schiacciata, riflusso del layout sul filtro e la specie, una lenta deriva nella testata, e lo scheletro kmail esistente della console invece di uno spinner. Tutto questo è passato su prefers-reduced-motion. Tre bug trovati durante la scrittura, ciascuno fissato qui: confronto entrare?.orgId al dettaglioOrg?.id non è definito == non definito quando entrambi sono assente, e poi legge .mode fuori nulla; window.open con noopener restituisce null, quindi una scheda pre-chiamata non potrebbe mai essere navigata (il riferimento apritore è sequestrato invece, che è la stessa protezione); e un callback successivo in la catena di dipendenza dell'effetto fetch potrebbe trasformare un re-render in un refetch loop, quindi gli helper del messaggio vengono letti attraverso una ref.
· SecurityService
Lasciate che un membro abbandoni la propria adesione
La pagina di rete ha offerto "Unsubscribe" da quando è stato scritto. Ha aperto una finestra di dialogo di conferma e poi ha detto che la funzione sarebbe stata implementata presto, perché non c'è stato un punto finale dietro di esso — un membro invitato in un organizzazione non aveva modo di uscire che non ha attraversato un amministratore. POST /api/security/org/{id}/leave agisce solo sulla riga del richiedente, così non prende alcun diritto di sé: nessuno può chiamarlo per rimuovere nessun altro. Disattiva piuttosto che cancella. recordState.isActive è il soft-delete bandiera, così la fila sopravvive per l'audit e può essere ripristinato, e l'occupazione ciclo di vita su TeamMember.teamMembershipStatus è un asse diverso che questo deliberatamente parte da solo. Ogni percorso di sessione, roster e notifica E' il termine base per cui e' che... stessa bandiera, in modo da cancellarlo termina la presenza di chat, menzione e assegnazione eleggibilità senza ulteriori lavori. Sessioni già aperte contro l'iscrizione è revocata il modo di off-boarding lo fa, quindi l'accesso termina ora piuttosto che al TTL scorrevole. Tre membri non possono essere ammessi, ciascuno rifiutando un diverso tipo di danno: l'organizzazione che serve la richiesta (lasciandolo fili che sessione di richiesta), l'organizzazione originale del chiamante (dove l'utente conto è ancorato, senza ri-invitare il percorso indietro), e uno il chiamante possiede (che lo lascerebbe con nessuno in grado di amministrarlo).
· kamo-internal
Ricostruire /hr/legal-compliance come registro legale
La superficie era una striscia di schede MUI su due griglie AG. Pacchetti resi come sette colonne di conteggi e una cella di azioni di sei pulsanti icona non labelled, tre di che si è disattivato senza dire perché — l'unico modo per imparare che Pubblica ha bisogno di un legante era quello di aggiungere uno e guardare il pulsante svegliarsi. - Un misuratore di conformità apre la pagina: ogni incarico nell'org come uno impilato bar oltre quattro secchi, costruiti da sette conteggi totali solo perché DocsService non ha un endpoint aggregato. Le sue file leggendarie sono i controlli (la barra stessa è decorazione ad aria nascosta), e scegliendo uno filtra le assegnazioni attraverso ?bucket= — un link che un amministratore può inviare. - I pacchetti diventano un mazzo di carte che trasportano quello che un tavolo doveva cadere: la versione Come un medaglione, il vero... spina dorsale, pubblico, copertura, e l'impronta del contenuto pubblicato come monogramma. Ogni blocco l'azione rimane visibile e afferma la sua ragione come testo, mai come un tooltip su un controllo disabili. - Le assegnazioni rimangono una griglia, ora con una guida statale, il membro sopra il loro indirizzo, avanzamento della firma come barra, e uno stato SENTENZA nel proprio stato colore invece di un chip grigio. - Un vocabolario finale (pubblicare / consegnare / ristampare / ritirare), corrispondente I verbi del backend. Il colore di interazione è --legal-brand in tutto, per la regola di casa che il marchio possiede ogni anello e profilo di messa a fuoco; --legal-seal soggiorni per identità e stato, quindi un Il white-label org vede ancora il suo colore in questa pagina. Ogni regola che la superficie disegna è una funzione pura in app/lib/legalRegistry.ts con 36 test, tra cui una prova che i quattro secchi coprono i sette incarichi stato esattamente una volta — senza di esso uno stato aggiunto al backend sarebbe sceso il metro silenziosamente, lasciando la barra piena larghezza e parte dell'org non conteso. buildAssignmentQuery ora invia gli stati come param REPEATED quindi un secchio intero è uno richiesta; un CSV arriverebbe a DocsService doppio codificato come un token letterale.
· kamo-marketing
Gioca la bobina kamo13
Punta l'eroe alla scala della nuova bobina, MP4 fallback e poster. La fonte è spec-identico a quello che sostituisce (H.264 720p24, 66s, ~30 MB), quindi nulla circa il giocatore o la scala cambia.
· KlusterServices
Scambiare la bobina dell'eroe a kamo13
kamo13.mp4 caricato su MinIO a pubblico/kamo13.mp4 (30 MB, sha256 174aaec6..., verificato date-identico alla fonte locale dopo il trasferimento — questo collegamento ha un storia di mangling grandi copie). I vecchi oggetti kamo12 sono lasciati al posto: nulla viene cancellato da MinIO, e sono il percorso rollback. La nuova fonte è spec-identica a quella che sostituisce — H.264 720p24, 3.56 Mbps video + 256k AAC, 66.005s — quindi la scala non ha bisogno di retuning. No. 1080p rung; la fonte è 720p e uno sarebbe puro su scala. Le chiavi di lavoro codificate sulla SHA-256 del byte sorgente, quindi indicandolo ad un nuovo l'oggetto è sufficiente per farlo ri-encode; costruirà pubblica/hls/kamo13/ e pubblicare kamo13-poster.jpg/.webp sulla prossima distribuzione. Inoltre documenta una caverna di ordinazione dello swap esposto: la fase di distribuzione rotola il Distribuire fuori prima di eseguire il lavoro di codifica, quindi il nuovo pod inizia mentre loop.mp4 appartiene ancora alla bobina precedente. Solo il canale /live/ è influenzato, e solo da segmento jitter — la /vod / scala giochi homepage è costruito dopo e non è interessato. Un riavvio del rollout lo prende.
· kamo-internal
Ricostruire /hr/training come console di allenamento
La scheda era due griglia AG sotto una barra di linguetta: accurate e mute. Ora si apre sull'intero obbligo di formazione dell'org e lascia agire un amministratore i vuoti. - Una testata la cui rotaia coorte è la popolazione di assegnazione — passata, in ritardo, in corso, non iniziato, scusato — dimensionato in proporzione e a carico. Ogni segmento di deep-link in Assegnamenti pre-filtrati, così il grafico è la strada verso il lavoro piuttosto che una foto di esso. - La versione ledger: un tick solido per versione pubblicata con il live uno acceso, continuando in una borchie schiacciata quando il progetto è passato E' cosi'. Un corso pubblica come versioni e gli studenti sono abbonati a quello sono stati inviati — che era in precedenza una linea di testo leggere una riga in una riga tempo. Guidato sia dalla galleria che dal tavolo, quindi le due viste condividono un vocabolario. - I corsi diventano una galleria di carte con ricerca, chip di stato, ordine e copertura arte; la griglia rimane dietro un gioco per la scansione densa. Sia letto da ShapeCatalog, quindi non possono essere d'accordo su quali partite. - Le assegnazioni guadagnano la ricerca, multi-seletto con rinforzo/extend/excuse/ annullare, e CSV esportazione. Ogni pulsante di massa indica quanti dei selezione può effettivamente toccare — un Ricorda 40 che le mail 31 è una bugia l'amministratore poi agisce su. - Un terzo sub-tab, Insights: anelli passa-tasso e medio-score, il inseguire la coda, i membri in piedi, attività recente. Tutto proviene da il blocco di formazione di /api/docs/hr/summary, che il HR Panoramica già chiama; DocsService è intatto. - coverImgId è stato sul DTO e nel payload di salvataggio dal modulo spediti con nulla che possa impostarlo e niente che lo legga. The l'editor ottiene il picker che rende il campo reale. Quattro difetti fissati lungo la strada: - L'editore è stato disabilitato su ogni riga del catalogo e sempre stato. pubblicareBlockedReason passeggiate course.items, ma lista() mappe righe attraverso toDto (corso, falso), che non li imposta mai — così l'elemento camminare ha visto un elenco vuoto e restituito 'vuoto' per ogni corso, compresi quelli che pubblicare perfettamente dall'editor. catalogo PubblicaBlockedReason cancelli su i conti che l'elenco invia; i rifiuti per-questione rimangono nel editor, dove esistono le domande. - Il messaggio della conferma di cancellazione era il corpo a stato vuoto, incollato, così ha letto 'Acquista un corso da diapositive e scelta multipla test, poi assegnalo alla tua squadra.' - I chip di stato di assegnamento hanno reso status.replaceAll(' ','') database enum, in inglese, in tutte le 22 località . - Due chiamate finestra.prompt() hanno raccolto una scadenza di conformità e una rinuncia ragione in una scatola di sistema operativo non stile. Entrambi sono dialoghi reali ora; il waiver la ragione è ciò che un revisore legge. Regole dal vivo in app/lib/trainingConsole.ts sotto 36 test: vitest here run nell'ambiente nodo e raccoglie solo .test.ts, quindi una regola scritta dentro un .tsx è una regola senza prova. Le corde sono atterrate per la prima volta in kamo-translation-dictionary b5ed84af.
· kamo-marketing
Filare il banner dell'offerta, fissare i morti Per saperne di più link e il campo esagonale
Cinque correzioni, tutte quelle cose che sembravano presenti ma non hanno fatto niente. OFFERTA BANNER — HeroOfferBanner era codice morto; niente importato. Restaurato, ma guidato da qualsiasi cosa l'API prezzi in realtà restituisce piuttosto che dal vecchio hardcoded PROMO CODE plus FALLBACK PROMOTION. È per questo che è morto: la data di fine passato e il componente poi reso nulla per sempre. Pubblicazione una promozione nel backend lo fa apparire sul prossimo rivalidate e lasciarlo lapse lo rimuove, senza schierare in entrambi i modi. Promozioni già tornate accanto ai piani, quindi il banner non costa nessuna richiesta in più. La selezione filtri a FLAT PER SEAT, che è una guardia di correttezza piuttosto che una preferenza: la copia del banner legge "Salva $N/user", quindi un PERCENTAGE promo renderebbe "$20 / utente" per quello che è veramente 20% off. Richiede anche un endDate reale — un conto alla rovescia ha bisogno di qualcosa a cui contare, e il precedente versione falsata con l'anno 2099. LEARN MORE — L'indicatore su ogni scheda home è stato un nudo <div> su Niente. Ora è un vero Link con un allungato:: dopo così l'intera carta è l'area colpita, puntando ad un nuovo per-categoria ancora su /features. I sei le fasce di categoria sono un contratto pubblico ora, e sono commentati come tali. HEXAGON FIELD — `.hexagon-grid` dichiarato `posizione: relativa` mentre la sua unica l'utilizzo richiede `absolute inset-0` via utilità. Questo file si carica dopo il Importazione del vento, quindi a pari specificità il foglio di stile ha vinto: l'elemento collassato a zero altezza e ritagliato il proprio:: prima attraverso `overflow:hidden` e `contain:paint`. Il campo non aveva affatto reso nulla. VIDEO CUT OFF — il telaio della bobina sovrasta la vista di 1,5-5rem in modo da visibilmente fuori schermo. Che legge bene per la decorazione e male per un prodotto demo, dove la striscia ritagliata è contenuto qualcuno è destinato a guardare. Ora finisce arrossire con il bordo: ancora nessun gutter destro, quindi continua a leggere come un finestra piuttosto che una carta, ma l'intera bobina è visibile. Inoltre: un controllo di riavvio accanto al pulsante del suono, e integrazioni 64 -> 100+. Entrambi i controlli video sono icon-solo ora, che è una correzione piuttosto che uno stile scelta. eroeVideoMute/eroVideoUnmute mancavano dalla traduzione dizionario, quindi il prossimo-intl ha reso il percorso chiave grezzo — HTML dal vivo stava servendo E l'eroe ricostruito lo ha trasformato in... testo visibile sul pulsante. Le chiavi sono aggiunte a kamo-traduzione-dizionario (f4d2b65b); le icone più un'aria-label mantengono un gap di copia dal diventare mobili. Il conto alla rovescia 50ms tick è 20 renders un secondo per sempre su una pagina che è anche decodifica video, così chiunque chiede di movimento ridotto ottiene un orologio 1s e perde l'unità milliseconda.
· kamo-login
Ricostruire il pannello di benvenuto intorno a una linea di stato live
La colonna di sinistra era un titolo generico, una latitudine di sicurezza e un mucchio di decorazione dell'ambiente (freccia del punto, orbi galleggianti, un dash pulsante). Sostituiscilo con un componente WelcomePanel condiviso: un blocco Geist a contrasto di peso, una linea di capelli regola, una singola riga di copia di orientamento, e una ferrovia monospaziale che riporta che cosa è realmente vero circa il sign-in — se la connessione è crittografata, che vi ospitate, e dove siete nel flusso. La schermata di recupero utilizza lo stesso pannello con la propria copia e mappatura passo in modo da i due schermi rimangono una superficie.
· kamo-marketing
Ricostruire l'eroe come una divisione editoriale con la bobina come un'apertura
La bobina era un rettangolo arrotondato centrale con un'ombra di goccia — un oggetto posto sul disegno piuttosto che una parte di esso — ed era la cosa SOLO nell'eroe: ff04781 cancellato il distintivo, titolo, descrizione, CTA e statistiche quando è atterrato il video, che ha lasciato HeroHeadline. tsx orfano e dieci copie chiavi inutilizzate. Quindi... la sezione non aveva tesi e nessuna chiamata all'azione, che è la maggior parte del motivo per cui legge come un quadrato nel mezzo dello schermo. Ora una composizione a due colonne: sopracciglio, titolo, lede, CTA e free-forever nota a sinistra; la bobina a destra, in esecuzione PAST a destra bordo della vista. Tre cose fanno l'integrazione: - gli angoli del telaio sono smussati a 45°, la stessa geometria dell'esagono campo già dietro di esso, invece del confine-radio ogni eroe SaaS utilizza; - una leggera rotaia lungo il bordo sinistro porta l'oliva dell'intestazione passaggio di viaggio, legando le due metà della divisione insieme; - sanguina fuori schermo piuttosto che fermarsi corto, quindi non c'è quarto bordo per farlo leggere come carta. La sezione e' nascosta e' cio' che la clip. Il bordo è un gradiente 1px che mostra attraverso la scatola di imbottitura del genitore, perché clip-path taglia i bordi al chamfer. Entrambe le scatole ripetono il poligono — clip-path non eredita, e una scatola interna quadrata pokes suoi angoli attraverso. Particolare lavoro: il controllo del suono si è spostato in basso a sinistra, dal momento che il telaio è bordo destro è deliberatamente fuori schermo e un controllo ancorato ci sarebbe Non raggiungibile. Una ferrovia di progresso lungo il fondo mostra la posizione nel loop — l'unica cue che il rullo è finito piuttosto che uno sfondo animato. Le macchie sono una regola della linea di capelli sotto la composizione, senza conteggio: quattro figure da corsa su carico competere con la bobina al momento dovrebbe essere Guardato. "Watch Demo" è andato come CTA perché la bobina accanto al pulsante è la demo; il collegamento secondario riutilizza l'etichetta "View Pricing" della striscia CTA e target in modo che la pagina nomina l'azione in un modo. Nessuna nuova copia: ogni stringa già esisteva in en.json. i18n/request.ts già immerge ogni locale con l'inglese, quindi le etichette statistiche mancanti da 21 localis rendering in inglese fino a quando il dizionario CI cattura. Codice morto rimosso lungo il percorso: un div accento-glow keyed off a `group-hover` senza `group` antestor, un video inutilizzatoContainerRef, e oggetti-contengono su un 16:9 sorgente all'interno di un telaio 16:9.
· kamo-marketing
Giocare la bobina come una scala HLS adattativa invece di un 31,8 MB MP4
L'eroe ha indicato un nudo <video src> in un singolo 3,9 Mbps 720p24 MP4, quindi un telefono su una connessione debole scaricato esattamente lo stesso flusso di un desktop fibra, e continuava a scaricare dopo che il visitatore scorreva passato. Ora suona il 720p/540p/360p scala costruita da KlusterServices' eroe-hls-encode job: il giocatore prende il rung la sua connessione porta e fetches ~4s segmenti solo mentre guardi. La rung 360p è di 5,4 MB contro l'originale 31,8 MB. Il gioco si muove dal JSX e in un effetto perché scegliendo tra HLS e il MP4 è una decisione di runtime. Safari e iOS ottenere la playlist nativo (iOS non ha estensioni di origine media, quindi hls.js non è un'opzione lì); tutti gli altri lo ottiene attraverso un hls.js importato dinamicamente che rimane fuori il fascio di ingresso. Un errore hls.js fatale, un browser non supportato, o un mancato chunk caricare tutta la terra sul MP4 originale, che è ancora la fonte della verità. Il <video> non comporta deliberatamente alcun `src` — dichiarando che si avvierà 31.8 MB progressivo scaricare questo cambiamento esiste per rimuovere. hls.js diventa una dipendenza diretta. Era presente solo via transitoria @react-tre/drei, che non è più in package.json affatto; npm install ha potato i suoi 67 pacchetti di avanzi dal file di bloccaggio. Niente in app/ Importazioni drei, tre o reag-flags mondiali (documenti FlagEmoji.tsx che sostituiscono l'ultimo di quelli di proposito). L'attributo poster inizia anche a lavorare: ha fatto riferimento public/kamo12-poster.jpg da quando questo componente è stato scritto, ma l'oggetto mai esistito e l'eroe reso vuoto fino al primo frame decoded. The codificare il lavoro ora lo pubblica.
· KlusterServices
Distribuire un server di streaming e una scala ABR per la bobina dell'eroe
MediaMTX serve canali dal vivo su HLS, WebRTC, RTSP e RTMP, e loop bobina di marketing come canale sempre disponibile tramite sempreAvailableFile — no ffmpeg sidecar, e qualsiasi editore RTMP / RTSP trasparente prende il canale e ritorna al rullo su disconnessione. Il video homepage stesso NON è servito attraverso MediaMTX. MediaMTX è un live server senza modalità VOD; looping di un file in un canale live cadrebbe ogni visitatore nel mezzo della bobina invece della prima cornice. Quindi... eroe-hls-encode-job costruisce un 720p/540p/360p Scala HLS dello stesso MP4 e lo pubblica a MinIO, percorso come /vod/ sull'host di streaming. Misurazione contro il 31.8 MB singolo bitrate originale: 18.8 MB a 720p (SSIM 0.989), 9,8 MB a 540p, 5.4 MB a 360p, in ~4s segmenta il giocatore fetches solo mentre guarda. Il lavoro è ETag-idempotent, quindi non funziona su ogni distribuzione che non è un video swap, e pubblica anche public/kamo12-poster.jpg — citato da homepage da quando è stato scritto, ma un 404 fino ad ora. Percorsi in diretta su stream.* e tema.*: tema. certificato per ogni inquilino, quindi questo funziona oggi e il modulo stream.* attiva da solo una volta che esiste un record DNS. Il percorso WebRTC corrisponde a un prefisso. — MediaMTX serve WHIP sulla stessa porta, e una partita prefisso sarebbe pubblicare un endpoint ingest non autenticato su Internet. Questo tocca .forgejo/, che costringe FULL=true e un full-cluster riconciliare. Prima verificata per la procedura documentata: si applica kubectl diff su tutti i 112 obiettivi è pulito su 104; cinque sono questi nuovi file, due sono i nats/mail bootstrap Jobs il flusso di lavoro elimina deliberatamente e riapplica, e mail/mysql-permissions-job.yaml non riesce come un lavoro completato immutabile, che il flusso di lavoro già tollera con `| echo`. Ogni obiettivo di cancellazione kubectl era confermato assente.
· KlusterServices
Workstation-side mount script per la condivisione KAMOTEAM
NFSv4 sopra il NodePort, montato morbido con un automount sistemato in modo da unreachable cluster restituisce un errore invece di appendere ogni processo che tocca il cartella, e così il computer portatile ancora stivali normalmente quando fuori la LAN.

Il tuo universo ti sta aspettando.
Aggiungi i prodotti quando sei pronto: nessuna migrazione, nessun nuovo login, nessuna richiesta di pagamento.
Gratis per sempre — nessuna carta di credito richiesta