<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[cunnin.gs]]></title><description><![CDATA[Guile | Craftiness | Deviousness | Slyness | Trickery | duplicity | Shrewdness | Astuteness]]></description><link>https://cunnin.gs/</link><image><url>https://cunnin.gs/favicon.png</url><title>cunnin.gs</title><link>https://cunnin.gs/</link></image><generator>Ghost 5.75</generator><lastBuildDate>Mon, 06 Apr 2026 21:11:40 GMT</lastBuildDate><atom:link href="https://cunnin.gs/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Vores digitale sundhed]]></title><description><![CDATA[<p>Denne post er mine personlige observationer, fra en meget lav taburet, l&#xE6;ngst omme bag i k&#xF8;en af mandagstr&#xE6;nere, og sikkert noget, du b&#xF8;r springe over, hvis du har en l&#xE6;ngerevarende humanistisk uddannelse, eller identificerer dig med en af de 12-16</p>]]></description><link>https://cunnin.gs/vores-digitale/</link><guid isPermaLink="false">68c9e7840eee610007432a82</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Wed, 17 Sep 2025 00:19:23 GMT</pubDate><content:encoded><![CDATA[<p>Denne post er mine personlige observationer, fra en meget lav taburet, l&#xE6;ngst omme bag i k&#xF8;en af mandagstr&#xE6;nere, og sikkert noget, du b&#xF8;r springe over, hvis du har en l&#xE6;ngerevarende humanistisk uddannelse, eller identificerer dig med en af de 12-16 grupper, som det lykkes mig at forn&#xE6;rme direkte eller indirekte over de f&#xF8;lgende ca 1000 ord &#x2013; s&#xE5; er du advaret!</p><p>Ingen, slet ikke jeg, kunne forestille sig, hvilke samfundsomv&#xE6;ltende konsekvenser introduktionen af iPhone for 18 &#xE5;r siden ville f&#xE5;. Det var ny teknologi (reelt var det blot ny indpakning, men hvilken indpakning)! I en r&#xE6;kke iterationer gik <em>smartphonen</em> fra at v&#xE6;re en gimmick til at v&#xE6;re et uundv&#xE6;rligt v&#xE6;rkt&#xF8;j, og kolossale v&#xE6;rdier skiftede over ganske f&#xE5; &#xE5;r ejere. Nokia og Blackberry blev 2 af de f&#xF8;rste ofre, men langt fra de sidste. </p><p>Muligheden for at n&#xE5; et globalt marked, hvis du blot byggede dit program (din app) p&#xE5; en s&#xE6;rlig m&#xE5;de, frigjorde helt overv&#xE6;ldende m&#xE6;ngder af energi, hitte-p&#xE5;-somhed, og hvad der n&#xE6;rmest syntes som en ustoppelig kollektiv str&#xF8;mning sendte chokb&#xF8;lger igennem verdenssamfundet. Nu kunne alle snakke med alle, n&#xE6;rmest uanset hvor p&#xE5; kloden de befandt sig. Nye anvendelsesm&#xE5;der blev introduceret hurtigere end lovgivning kunne f&#xF8;lge med. </p><p>Ofrene var mange - nogen mere selvforskyldte end andre. Den trykte presse (som den blev kaldt) mistede kontrollen med adgangen til at sprede et budskab. Begrebet SoMe opstod som en betegnelse for en gruppe digitale v&#xE6;rkt&#xF8;jer, som netop fratog den trykte presse en stor del af kontrollen. Internettet havde i &#xE5;rtier skabt et fundament og en r&#xE6;kke v&#xE6;rkt&#xF8;jskasser, men det var f&#xF8;rst i slutningen af 00-erne at potentialet blev forl&#xF8;st. </p><p>V&#xE6;rdik&#xE6;den fra producent til forbruger r&#xF8;g ogs&#xE5; ind under bussen. Ingen fysisk butik kunne konkurrere med forbrugerens sofahj&#xF8;rne og en rygende varm kop te! F&#xF8;rst gav specialbutikkerne fortabt, siden led regionale k&#xE6;der samme kranke sk&#xE6;bne, og selv store internationale butiksk&#xE6;der m&#xE5;tte kaste h&#xE5;ndkl&#xE6;det i ringen. Standardiserede produkter is&#xE6;r elektronik var et nemt offer, men b&#xF8;ger, husger&#xE5;d, selv dagligvarer. Enorme strukturelle omkalfatringer i samfundet blev afviklet stort set uden at nogen l&#xF8;ftede en finger. Vel - der var naturligvis det s&#xE6;dvanlige korps af pessimistiske samfundsforskere som altid r&#xE5;ber &apos;ulven kommer&apos;, men alle var optaget af at stirre m&#xE5;bende, imponeret, og ganske fortabt ned i deres ene h&#xE5;ndflade, hvor det, der n&#xE6;sten virkede som den enkeltes eneste reelle livsnerve, l&#xE5; og glimtede, og med voksende hastighed gav sm&#xE5; <em>klimp</em> fra sig, n&#xE5;r en ny notifikation meldte sin ankomst.</p><p>Familien var n&#xE6;ste offer. Den havde v&#xE6;ret igennem en r&#xE6;kke opslidende n&#xE6;rd&#xF8;dsoplevelser siden kvinderne smed sleven og glattejernet i 60&apos;erne. Det skabte velstand men ogs&#xE5; trol&#xF8;shed. Arbejdspladsen blev langsomt men sikkert en erstatning for familien. Nu var familien en pr&#xE6;station - noget man skulle &apos;pr&#xF8;ve&apos;. B&#xF8;rn blev projekter, som alle dr&#xF8;mte om, og n&#xE5;r man s&#xE5; havde pr&#xF8;vet det, kunne den checkbox checkes af, og s&#xE5; var det videre! Fokus &#xE6;ndrede sig fra <strong>vi</strong> til <strong>jeg</strong> - og det gjalt ogs&#xE5; b&#xF8;rnene. Derfor kom den digitale sutteklud som en k&#xE6;rkommen redning. Med lydb&#xF8;ger og tegnefilm var det pludselig &quot;overkommeligt&quot; at bes&#xF8;ge familien i den anden ende af landet. &quot;Sk&#xE6;rm&quot; blev et gode og b&#xF8;rnene var hurtige til at efterabe, hvad de s&#xE5; deres for&#xE6;ldre g&#xF8;re: stirre i h&#xE5;ndfladen. Velstanden, bedre uddannelse, og de store &#xE6;ndringer i familiem&#xF8;nsteret og jeg-kulturen fjernede ogs&#xE5; behovet for religionen, og med ud med badevandet r&#xF8;g derfor ogs&#xE5; etik og moral; hvis ikke det bragte <em>mig</em> videre, var det ligegyldigt. Kirkerne overlevede, fordi pr&#xE6;sterne valgte at blive <em>event managers</em> &#x2013; </p><p>Et v&#xE6;rkt&#xF8;j, der f&#xF8;lger dig d&#xF8;gnet rundt, og som du interagerer med &gt;50 gange i dine v&#xE5;gne timer. Funktioner som m&#xE5;ler og vejer dig, afbilleder dig, viser dig frem, definerer dig. Var det s&#xE5; blot <em>dig</em>, men det er alle dem, du kender, og alle dem, du f&#xE6;rdes iblandt. Dine naboer, arbejdskolleger, medlemmer i din fitness klub, alle! Alle ved hvad du lavede i weekenden, hvor fuld du blev, hvad du spiste i g&#xE5;r til aftensmad, og hvad du mener om <em>personer, med s&#xE6;rlige kognitive f&#xE6;rdigheder</em>.</p><p>Med moralen og etikken ude af billedet, blev det n&#xE6;ste offer sandheden. Med det rigtige lys og lidt plastisk hj&#xE6;lp blev det nemmere at klare sig i &quot;pisse-l&#xE6;ngst&quot; konkurrencen. Manglende skolef&#xE6;rdigheder var ikke en hindring. B&#xF8;rn kunne h&#xF8;res dr&#xF8;mme &quot;om at blive kendt eller rock-sanger&quot;. Med sandheden p&#xE5; sikker afstand blev det n&#xF8;dvendigt at justere en r&#xE6;kke dele af virkeligheden, som ikke passede ind i billedet. Fortidens synder om du vil. <em>Cancel culture</em> var en naturlig l&#xF8;sning.</p><p>Denne opremsning og <em>tour de force</em> i samfunds&#xE6;ndringer de seneste 50-60 &#xE5;r har m&#xE5;ske efterladt dig med f&#xF8;lelsen af at bladre i et nummer af Illustreret Videnskab eller en reklame for en tryllekunstner &#x2013; for det er jo l&#xF8;srevne fragmenter af en virkelighed, der er alt alt for meget mere kompleks til at jeg overhovedet kan tillade mig at n&#xE6;vne nedslagene og ofrene; og s&#xE5; forn&#xE6;rmer jeg jo b&#xE5;de kvinderne, ateisterne, og sikkert en 3-4 andre grupper af kr&#xE6;nkelsesparate digitalt overv&#xE6;gtige. </p><p>Uanset s&#xE5; kan det n&#xE6;ppe undre dig at regeringen og med den, den regerende elite, og embedsv&#xE6;rket, har udset sig privatlivet som det n&#xE6;ste offer. Argumenterne st&#xE5;r i k&#xF8;. Kriminalitet, terror, krig, samfundsnedbrydende virksomhed. Truslerne l&#xE6;ner sig som h&#xF8;je krumme popler langs en smal all&#xE9; ind over dem, der gerne vil hj&#xE6;lpe os alle med at f&#xE5; et godt liv. </p><p>De t&#xE6;nker p&#xE5; vores sundhed &#x2013; og nu (omsider) ogs&#xE5; vores digitale sundhed. Det, de regner med, er at i r&#xE6;kken af ofre, har (n&#xE6;sten) en hel generation nu levet i et udstillingsvindue, s&#xE5; hvad enten de kigger med eller lader v&#xE6;re, det er s&#xE5;dan set temmelig ligegyldigt, for der ikke er meget tilbage, som ikke allerede er udbasuneret, filmet, beskrevet, podcast&apos;et. Den unge generation i dag er vant til at der f&#xF8;lger et kamerahold med rundt, ja faktisk bliver de skuffede, hvis der ikke g&#xF8;r; deres for&#xE6;ldre har nemlig <em>curlet</em> dem, s&#xE5; selv fyldet i fl&#xF8;debollerne var aftalt med for&#xE6;ldregruppen i indskolingen, p&#xE5; forh&#xE5;nd.</p><p>&#x2013; men hvordan var det s&#xE5; lige med 263&apos;eren? Brevhemmeligheden? Det er en rigtig besv&#xE6;rlig paragraf, som det sikkert kommer til at tage (eller m&#xE5;ske allerede har taget) Kammeradvokaten et p&#xE6;nt rundt 7 cifret bel&#xF8;b at f&#xE5; til at forsvinde ned i en s&#xE6;k med d&#xE5;rlige undskyldninger, forblommet tale, og et gavmildt skv&#xE6;t k&#xF8;nr&#xF8;g. Jeg er sp&#xE6;ndt p&#xE5; om man v&#xE6;lger at g&#xE5; med &quot;omsorg for barnet&quot; eller trykker en s&#xE6;rlig blanket, som dommere kan underskrive <em>a posteriori &#x2013; </em>sundt bliver det dog nok n&#xE6;ppe!</p>]]></content:encoded></item><item><title><![CDATA[License to --]]></title><description><![CDATA[<p>Danes will be getting a new drivers license - at some time in the future, as <a href="https://netcompany.com/?ref=cunnin.gs" rel="noreferrer">Netcompany</a> has just won a tender for building and managing just that: a drivers license system. Which made me contemplate how such a system would actually be designed.</p><p>[ I will use &quot;he&quot;</p>]]></description><link>https://cunnin.gs/license-to/</link><guid isPermaLink="false">686b9602029b6e000777620d</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Mon, 07 Jul 2025 16:13:33 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1538356534-86d10acc9fdf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDd8fGxpY2Vuc2V8ZW58MHx8fHwxNzUxOTA0NzU2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1538356534-86d10acc9fdf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDd8fGxpY2Vuc2V8ZW58MHx8fHwxNzUxOTA0NzU2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="License to --"><p>Danes will be getting a new drivers license - at some time in the future, as <a href="https://netcompany.com/?ref=cunnin.gs" rel="noreferrer">Netcompany</a> has just won a tender for building and managing just that: a drivers license system. Which made me contemplate how such a system would actually be designed.</p><p>[ I will use &quot;he&quot; when in fact acknowledging the non-masculine, non-binary he/she/it/they<br> &#x2013; but for simplicity restraining from using all, some, intermittently. ]</p><h2 id="abstraction">Abstraction</h2><p>A legal body [ a driver, case in question ] will a some point in time be required to document a certain set of attested competencies (his ability to command a vehicle). <br>This requirement presents 2 problems. </p><p>Authentication &amp; authorization</p><p>Solving both, the driver could (always) travel with the attester or he could &apos;facetime&apos; the attester when required to document and hand the phone to the requesting body. This would redirect the authorization (and authentication) to the attester, and not further the case!</p><h2 id="digital-sovereignty">Digital sovereignty</h2><p>Under EU 2016/679 any system to control and/or process personal data is required to be designed for privacy - with the legislative intent being sovereignty as a matter of fact.</p><p>Some licenses are more sensitive than others. A license to operate (read 00- like in 007) within the borders of another country will be rather sensitive. A drivers license lesser so but still quite sensitive depending upon the driver&apos;s demonstrated abilities - ie if he manages to compile a long list of &quot;likes&quot; the license will possibly be quite sensitive at least in other situations: applying for a job as a chauffeur eg.</p><h2 id="autonomy">Autonomy</h2><p>So far I am pretty sure you have considered legal bodies to be biological ones only! An extremely edge case as of writing, I concede, would be one in which an autonomous vehicle (AV) gets &quot;pulled over&quot; by another AV.</p><p>The example most likely will be reality &#x2013; albeit in an unforeseeable future, but suffice to say a requirement for automaticity is obvious.</p><h2 id="design">Design</h2><h3 id="authentication">Authentication</h3><p>The legal body (autonomous entities no doubt will become legal bodies, eventually) should present something he knows, something he has, and something he is &#x2013; and at least 2 of the 3 <em>artifacts</em> should be &apos;on record&apos;.</p><ol><li>the &quot;knows&quot; part could be a pin code, one that makes sense to the body in question, like a date, height, or other set of digits,</li><li>the &quot;has&quot; thing could be a public/private key &#x2013; when asked to encrypt a given word the result can be diff&apos;ed and hence validated,</li><li>finally the &quot;is&quot; issue could be biometric (robots could offer their MAC address of sorts),</li><li>and adding a one-time-password SMS/Text sent on request would further increase confidence in the authenticity of the legal body</li></ol><p>Why not use MitID (current authentication solution currently deployed by danish public offices and private enterprises alike)? One word: automation<br>The sketched solution can be automated and kept on one device, and integrated with other apps.</p><p><strong>Frontend</strong><br>Users could download an app with the authentication process being the main feature (or the authentication process could be integrated into other apps like fx a driver&apos;s license app). The pin-code will open the app (and send a request for the one-time-password) and allow the user to process the encryption of a data package provided by the requesting body (to accomplish item 2), further allowing the user to present either fingerprint or face to camera (accomplish item 3), and finally once the SMS arrives, the user will have a QR code to present to either a robot or another legal body to scan.</p><p><strong>Backend</strong><br>Each user will have his own event journal/<a href="https://cunnin.gs/tag/blockchain/" rel="noreferrer">witnessed ledger</a> and safe keep his data &apos;on device&apos;. Public systems will keep whatever data points they require in a similar construct. The essentiel (core) table will hold the user&apos;s <a href="https://cunnin.gs/implementing-the-witnessed-ledger/" rel="noreferrer">pigeon</a>, public key and the phone number to forward one-time-passwords to (the system should have a semi-self-service system allowing users to switch phone number when necessary), and possibly autonomous system&apos;s MAC address. Initial authentication could/should be required to perform at municipal or notary offices. </p><blockquote>In countries that keep a uniq ID on each citizen (in Denmark it is labeled CPR) any attempts to keeping this ID with the authentication core table should be avoided at all costs as this ID probably is spread across a huge number of systems and thus will be violating the user privacy severely!</blockquote><p>At initial authentication at a municipal office, presenting the officer with the ID would be acceptable but persisting the ID with the authentication not!</p><h3 id="authorization">Authorization</h3><p>Demonstrating attested competencies is easily added to this &apos;app&apos; as a &quot;plugin&quot; and adding a plugin for a driver license is no different.</p><p><strong>Frontend</strong><br>Any user of a smartphone could download a &quot;driver license reader&quot; and should be able to read the token of a driver license, and to submit a &quot;traffic incident ticket&quot; with his personal (and geo location) information attached, possibly some attachments like audio/video too.</p><p>Any further reading/writing below will follow this (happy path) workflow:</p><ol><li>requesting body will submit a request attaching the (pigeon) token - WD1CKM4NN</li><li>the user with the token receives a notification requesting his accept</li><li>the user accepts the action/request</li><li>the requesting body receives the information</li></ol><p>In some use cases (like if the user does not accept the action/request) the requesting body will have to elevate the request to a police office, and if not solved with the police as requester, elevate the request to the legal system - and eventually the user will lose his license.</p><p>Insurance companies should be able to read attested competencies as well as registered &quot;likes&quot; when submitting a token. They should be able to write insurance information.</p><p>Attesters should be able to read token and address information, and write competencies attested, and his token.</p><p>Police offices should be able to read and write all information.</p><p>Judicial offices should be able to read and write all information.</p><p><strong>Backend</strong><br>The core table holds the license number and the token attached to it, and the competencies attested, when and where attestation was given, and a token of the attester. A supporting (like) table holds the token of the user and a token of the body offering the &quot;like&quot;, when it was inserted and any consequences, possibly a time range for deactivation of the license, more. A further supporting table holds the token of the user, a vehicle ID, a token of the insurance company, and the time range where the insurance is valid, and insurance specifics.</p><h3 id="infrastructure-and-budget">Infrastructure and budget</h3><p><strong>What amount of infrastructure are we looking at?</strong><br>Building on Erlang/Elixir for a &apos;telecommunications kind of app&apos; (I am aware that the initial use cases does not prescribe any tele communications per se, but a number of use cases down the road may prove it to be a fitting decision) will allow for <a href="https://phoenixframework.org/blog/the-road-to-2-million-websocket-connections?ref=cunnin.gs" rel="noreferrer">upwards of 2mio connections</a> per server, which when considering the danish scope, would ask for 3-4 servers &#x2013; if all danish citizens were to be &apos;online&apos; at the same time. </p><p><strong>What budget will that ask for?</strong><br>Building out the infrastructure in 2-3 small clusters located in the north, the south, and the east, with a primary and a secondary/fallback server will set the budget to a mere 3-400.000/yr on hosting and maintenance.</p><p>Using Phoenix with Erlang/Elixir will afford a single source stack building backend and frontend in one project with extremely high availability and modest requirements on deployment. The client application could easily be deployed as a progressive web application.</p><p>Developing/building the 2 applications (authentication &amp; authorization) and the driver license plugin could be a 2-3man task for 6-12months and see costs top at 3.000.000DKK, and probably in the 250-400.000/yr for continues development.</p>]]></content:encoded></item><item><title><![CDATA[NUG - the business]]></title><description><![CDATA[<p>With the core implementation down we can turn our attention to the business side of things: the organizational considerations and the business model</p>
<h2 id="organizational-considerations">Organizational considerations</h2>
<p>Affording a possibly global tracking of asset ownership will have to address issues like <em>audit trail</em>, <em>accountability</em>, <em>verification</em>, <em>division of labor</em>, <em>antitrust</em></p>
<h3 id="audit-trail">Audit trail</h3>
<p>The</p>]]></description><link>https://cunnin.gs/nug-the-business/</link><guid isPermaLink="false">630f4bdbf402c3000747cb1a</guid><category><![CDATA[blockchain]]></category><dc:creator><![CDATA[Walther Højgaard Diechmann]]></dc:creator><pubDate>Mon, 07 Jul 2025 09:36:26 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1462045504115-6c1d931f07d1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE0fHxiYW5rfGVufDB8fHx8MTc1MTgxMjI1MHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1462045504115-6c1d931f07d1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE0fHxiYW5rfGVufDB8fHx8MTc1MTgxMjI1MHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="NUG - the business"><p>With the core implementation down we can turn our attention to the business side of things: the organizational considerations and the business model</p>
<h2 id="organizational-considerations">Organizational considerations</h2>
<p>Affording a possibly global tracking of asset ownership will have to address issues like <em>audit trail</em>, <em>accountability</em>, <em>verification</em>, <em>division of labor</em>, <em>antitrust</em></p>
<h3 id="audit-trail">Audit trail</h3>
<p>The NUG will have to afford an audit trail to introspect transactions. This tool probably will have to be a paid service in order to reduce the number of users wanting to &apos;follow the money&apos; but idealistically anyone with a terminal and enough time on their hands should be able to &apos;walk the audit trail&apos; all the way back to the very first transaction!</p>
<h3 id="accountability">Accountability</h3>
<p>Underwriters are the cornerstone within the NUG. They will have to be trusted with the right to declare assets, update assets, and retire them eventually. Constructs will have to be installed in order to secure their accountability. Underwriters will be able to &quot;create wealth&quot; and thus should be accessed thoroughly, and repeatedly!</p>
<h3 id="verification">Verification</h3>
<p>One way of accessing underwriters will require auditing their modus operandi - how they do business. How do they verify the existence of users assets!?<br>
In order to make good on threats of banning users that do not play nice but sells assets they do not have, try to lure assets from other users, etc, it will be necessary to identify every user beyond any doubt! Whether be it by some combination of biometrics, or otherwise.</p>
<h3 id="division-of-labor">Division of Labor</h3>
<p>The way the Domain Name System is run could be the way to go! Delegating responsibility for supervising, controlling, validating, and verifying underwriters to national bodies will create a hierarchical structure</p>
<h3 id="antitrust">Antitrust</h3>
<p>The NUG will protect itself from antitrust by being non-profit and allowing everyone access to its books.</p>
<h2 id="business-model">Business model</h2>
<p>The underwriters will be able to sell one basic service: controlling an asset, but the underwriters will eventually be able to do a lot more;</p>
<ul>
<li>issue loans</li>
<li>issue insurances</li>
<li>offer escrows</li>
<li>etc</li>
</ul>
<p>Services will be cheap (as it is really just software) but some customers will warrant personal service, better coverage, better security, faster services, etc.</p>
<p>Banks and other financial institutions are the obvious candidates to try to take control of this market, and with the trust invested in them by customers today, they probably will seize quite a large part of this market.</p>
]]></content:encoded></item><item><title><![CDATA[Network of Underwriters Guild]]></title><description><![CDATA[<p>This post describes the implementation design of the core function of the Network of Underwriters Guild (NUG); updating assets belonging to the providing party and the receiving party.</p>
<h2 id="procedural-overview">Procedural overview</h2>
<p>Underwriter sends</p>
<pre><code>{ 
  id: reference to underwriter&apos;s own ledger
  transaction: []
}
</code></pre>
<p>encrypted with their private key</p>
<p>The NUG software will</p>]]></description><link>https://cunnin.gs/network-of-underwriters-guild/</link><guid isPermaLink="false">630f4bdbf402c3000747cb18</guid><category><![CDATA[blockchain]]></category><dc:creator><![CDATA[Walther Højgaard Diechmann]]></dc:creator><pubDate>Mon, 07 Jul 2025 09:33:29 GMT</pubDate><content:encoded><![CDATA[<p>This post describes the implementation design of the core function of the Network of Underwriters Guild (NUG); updating assets belonging to the providing party and the receiving party.</p>
<h2 id="procedural-overview">Procedural overview</h2>
<p>Underwriter sends</p>
<pre><code>{ 
  id: reference to underwriter&apos;s own ledger
  transaction: []
}
</code></pre>
<p>encrypted with their private key</p>
<p>The NUG software will update the assets of two users, but with multi-billion rows, and 10&apos;s of thousands of underwriters, we have to make sure no one else will start updating our rows while we&apos;re at it!</p>
<p>We will have a <code>last_tick</code> table with one row telling us what the last generated tick is, and a <code>ticks</code> table holding the index<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> and an <code>assets</code> table holding the assets.</p>
<p>The NUG will call a function to generate a tick (a SHA256 hash) from</p>
<pre><code>{
  uct: underwriter&apos;s callTag
  ptick: previous tick
  time: timestamp
  id: the id provided by the underwriter
  hash: SHA256 hash of the transaction
}
</code></pre>
<p>once it has updated the assets -</p>
<pre><code class="language-sql">begin transaction;
--
-- provider
update assets set quantity = quantity - @quantity, lock = 1 
where callTag = @providing_user
and asset = @asset
and quantity - @quantity &gt;= 0
and lock = 0;
--
-- receiver
update assets set quantity = quantity + @quantity, lock = 1
where callTag = @receiving_user
and asset = @asset
and lock = 0;
--
-- we might want to validate the rows before we commit!
select * 
from assets 
where asset = @asset and callTag in (@providing_user,@receiving_user);
--
-- TODO verify 2 rows
update assets set lock=0
where callTag in (@providing_user,@receiving_user)
and asset = @asset
and lock = 1;
commit transaction;
--
-- update last_tick
begin transaction;
update last_tick set lock=1;
set @tick = build_tick(@uct, (select tick from last_tick) as @pt, @time, @id, @hash) 
insert into ticks (tick,callTag,id) values (@tick,@uct,@id)
update last_tick set tick=@tick, lock=0;
commit transaction;
</code></pre>
<p>and return that tick - and we&apos;re done!</p>
<p>This statement will get called thousands of times every second - it is imperative that it will perform at its optimum! Or that some other implementation will allow us to make a new last_tick perhaps like 100,000 times per second!<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>This last table will be VERY big - not in row size, but in number of rows! We will probably have to roll ticks off every now and then into week, month, year tables - like ticks_2018_02_01 or something. <a href="#fnref1" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn2" class="footnote-item"><p>If 7 billion people do just one &apos;buy/sell&apos; per day we are looking at 80K transactions/second. <a href="#fnref2" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
</ol>
</section>
]]></content:encoded></item><item><title><![CDATA[En route to the witnessed ledger]]></title><description><![CDATA[<p>What was the issues we took away from looking at the challenges now again?</p>
<ul>
<li>individual transactions be private (p)</li>
<li>open to justified scrutiny (o)</li>
<li>encourage transactions (t)</li>
<li>remove speculation (s)</li>
<li>Fees should be next to negible (f)</li>
<li>transactions occur in milliseconds (m)</li>
<li>incorporate security measures (S)</li>
</ul>
<h2 id="my-personal-ledger">My personal ledger</h2>
<p>(p)</p>]]></description><link>https://cunnin.gs/the-witnessed-ledger/</link><guid isPermaLink="false">630f4bdbf402c3000747cb15</guid><category><![CDATA[blockchain]]></category><dc:creator><![CDATA[Walther Højgaard Diechmann]]></dc:creator><pubDate>Mon, 07 Jul 2025 09:17:31 GMT</pubDate><content:encoded><![CDATA[<p>What was the issues we took away from looking at the challenges now again?</p>
<ul>
<li>individual transactions be private (p)</li>
<li>open to justified scrutiny (o)</li>
<li>encourage transactions (t)</li>
<li>remove speculation (s)</li>
<li>Fees should be next to negible (f)</li>
<li>transactions occur in milliseconds (m)</li>
<li>incorporate security measures (S)</li>
</ul>
<h2 id="my-personal-ledger">My personal ledger</h2>
<p>(p) Using a piece of paper to buil my personal ledger would not prove an unsurmountable problem! With only one piece of paper, I&apos;d be big on the (p) from the set go!</p>
<p>(o) If I was to carry the piece of paper on me all the time, I&apos;d have to be awake and give my consent, ie hand over the piece of paper; really big on (o)!</p>
<p>(t) I&apos;d be able to process as many transactions as I could possible write down - no problem with (t) at all!</p>
<p>(s) No one would speculate in my ledger - the volume of transactions and reach will be of no interest; (s) is super green!</p>
<p>(f) Unless I start charging myself - (f) is totally ok!</p>
<p>(m) Total fail on (m) - I&apos;d never be able to add transactions in milliseconds :(</p>
<p>(S) I guess I could start using invisible ink of sorts or writing down all numbers and text after using a computer to encrypt values/text, and I could lock the piece of paper in a safe between transactions. (S) is a 50/50 done!</p>
<h2 id="my-personal-digital-ledger">My personal digital ledger</h2>
<p>(p) If I was to choose a digital version of my ledger, I&apos;d still be good on the privacy as long as I keep the digital devise disconnected from anything else, but I&apos;d have to make sure no one else gets to my device - see (S)</p>
<p>(o) Handing over the device would work in a digital version too!</p>
<p>(t) Using a digital version would make it even faster/easier to add transactions, if the UI was done right!</p>
<p>(s) Speculation is still utterly out of the picture!</p>
<p>(f) No fees - no problem - except the cost of the device and the charging now and then. Fees are negligible; (f) is green!</p>
<p>(m) I could build a UI with buttons to automate various standard kind of transactions and have the software add these in what would translate to milliseconds. (m) within reach!</p>
<p>(S) Security starts getting in my way now! An electronic/digital device will require me to always keep it disconnected (no OS updates, etc). Some kind of access control would be preferable and adding encryption would fit the bill nicely! (S) could be greenish!</p>
<h2 id="a-digital-ledger-shared-with-my-brother">A digital ledger shared with my brother</h2>
<p>(p) Unless my brother did not know I shared the ledger with him, (p) is out!</p>
<p>(o) I would have to bring my brother to be able to legitimately hand over the ledger now; doable but inconvenient and highly uncommon; (o) mostly out!</p>
<p>(t) Transactions are now almost down to nil! Only when my brother and I are present and with us being located almost 100 miles apart (t) is close to impossible on a daily basis!</p>
<p>(s) With transactions down, volume is even worse, and reach is still next to nothing. (s) is super red!</p>
<p>(f) Fees went through the ceiling! Every transaction requires me or my brother to travel 100 miles and waste 4+ hours total. (f) is red!</p>
<p>(m) Speed died totally!</p>
<p>(S) Security did not get worse nor better. With an ACL both my brother and I could access the ledger and encrypting transactions keep (S) in the greenish!</p>
<h2 id="a-distributed-digital-ledger-shared-with-my-brother">A distributed digital ledger shared with my brother</h2>
<p>(p) no change - he knows of me and I of him - that&apos;s not private!</p>
<p>(o) With both of us keeping a copy of the ledger, I&apos;d need his consent to hand out my copy. That could be fixed with a digital signature. (o) would be doable!</p>
<p>(t) Transactions are getting better. We&apos;d both have to make a note of any transaction but neither of us did have to travel to accomplish it. (t) almost green!</p>
<p>(s) Still no transaction volume to speak off and lack of reach making it an investors nightmare!</p>
<p>(f) Another positive change! Fees are (almost) back to nil. My brother and I&apos;d have to let each other know when we would add transactions, but (f) definately within reach of &apos;green&apos;!</p>
<p>(m) Speed no where near milliseconds! I&apos;d have no idea when my brother would be available to add a transaction, which would force me to either wait or make a note of the transaction and keep the ledger on hold until my brother would report in!</p>
<p>(S) Security keep getting worse. Now we have digital signatures to deal with as well! But even so, (S) is kind of bordering green!</p>
<h2 id="the-distributed-ledger-through-a-3-person-shared-with-my-brother">The distributed ledger through a 3. person shared with my brother</h2>
<p>(p) Now the ledger is private - my brother does not need to know that the other owner is me per se. We are good on (p)!</p>
<p>(o) A digital signature to open the ledger for scrutiny still works through a 3. person. Question is whether this 3. person would have a say. Probably not if this 3. person would only be like hosting the ledger. (o) is mostly green!</p>
<p>(t) No changes to transactions. Still requires multiple updates but could be done asynchronously. (t) likely to be green!</p>
<p>(s) With a 3. person in the mix reach is gaining but volumes are still like none existing. (s) is reddish!</p>
<p>(f) Probably a slight decrease! With a 3. person hosting the ledger we&apos;d have to be prepared to &quot;dish up some Benji&apos;s&quot;, still (f) would be greenish!</p>
<p>(m) Speed could go up with adding transactions async, but I&apos;d have to build som mechanism into the adding of transaction process to wait for my brother to make a note of my transaction, and as such still be dangling on hold! (m) not quite there!</p>
<p>(S) Security still keep getting worse! Now there is this 3. person to care about! What do he/she know and learn? Now encryption is a must as is ACL&apos;s and somewhere to stow away the digital signatures once used. With that cleared, (S) would once again be mostly green!</p>
<h2 id="the-witnessed-ledger">The witnessed ledger</h2>
<p>Now we&apos;ve gone through a number of iterations which clearly identify the hurt points of shared ledgers. So far we have neglected one issue entirely in so far as the ledger was shared only with a trusted party (my brother)!</p>
<p>Now I widen my focus and start sharing with people I do not know &#x2013; as in trust with my life and fortune!</p>
<p>Now consider this: I build an algorithm into my wallet that will a) find a <em>common denominator</em> ie a mutual trusted party, or a path of trust between myself and the person I am going to share the ledger with, or b) if all else fail finds a <em>professional underwriter</em> ie a person or company that &quot;takes bets for a living&quot; (a bank might sign up for this).</p>
<p><strong>The happy path</strong> will incure no or negligible fees &#x2013; my (path of) trusted part(ies) will allow me to share my ledger and thus exchange data (possibly but necessarily payments) with the partner in question.</p>
<p><strong>The (not so) happy path</strong> will incure fees which will be known to me in advance and thus I can calculate with them and perhaps even browse for a least expensive path! The underwriters will have to know my <em>rate of success</em> and that of my partner in question too, perhaps. This could all be algorithms, system state, and machine learning!</p>
<p>I am left with not a single blockchain but a number of blockchains really! My private blockchain with blocks shared with those contacts I accumulate along the road.</p>
<blockquote>
<p>Directed Graph meet Blockchain!</p>
</blockquote>
<p>Now to the variables:</p>
<p>(p) It&apos;s private. I share my ledger with contacts whom I know, only. Know or come to know. The come-to-know part would be <em>underwriters</em> in particular.</p>
<p>(o) Collecting digital signatures from all parties involved is a daunting task but given to software certainly surmountable and with a confined set of parties (not an entire blockchain with millions of users) it is quite doable. (o) is green!</p>
<p>(t) Again; all parties in a path from A (me) to B (the partner in question be it Starbucks or the grocer around the corner) must make a note but this is what software is good at and will be able to handle swiftly. (t) is green!</p>
<p>(s) Even with more parties in the path from A to B reach is limited and volume relatively low. There will be room for speculation but that would be focused with the parties in question (say a big contractor, a building society, a cars manufacture, or some other contact with a huge volume of transactions, and a very big number of contacts; not the witnessed ledger as such). (s) is green!</p>
<p>(f) Fees will incure but most people trade in closed circles (barring vacations) and underwriters will act as <em>ice-breakers</em> or trust builders but after a few deals you will have a happy path even to that dodgy website. (f) would be greenish!</p>
<p>(m) Speed will easily be green in the happy path cases but slower in the not so happy paths! Over time speed will pick up - perhaps not 100ms on average but still; 800-1600ms with an odd one out when you must buy something off of a dodgy website is fairly green!</p>
<p>(S) You share the ledger with known parties only (and with <em>underwriters</em> who cannot afford to break their &apos;customers&apos; trust). Still security will always be the sore issue and it would be foolish to believe that the witnessed ledger is in the clear! Encrypting transactions, maintaining an ACL and log access, and use of digital signatures; it all adds to lowering the probability of theft, false representation, etc. (S) is mostly green!</p>
]]></content:encoded></item><item><title><![CDATA[Digital sovereignty]]></title><description><![CDATA[<blockquote>the ability of states, organizations, and individuals to control their digital destiny, including data, technology, and infrastructure, within their own sphere of influence.</blockquote><p>- s&#xE5; &quot;smukt&quot; formulerer en AI (ansat hos Google) digital suver&#xE6;nitet; p&#xE6;nt d&#xE6;kkende skulle jeg mene, men samtidig</p>]]></description><link>https://cunnin.gs/digital-sovereignty/</link><guid isPermaLink="false">6866ef83029b6e000777600f</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Thu, 03 Jul 2025 23:08:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1580422091842-314965d2bd4d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDM0fHxzb3ZlcmVpZ250eSUyMGRhbmlzaHxlbnwwfHx8fDE3NTE1ODQwOTB8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<blockquote>the ability of states, organizations, and individuals to control their digital destiny, including data, technology, and infrastructure, within their own sphere of influence.</blockquote><img src="https://images.unsplash.com/photo-1580422091842-314965d2bd4d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDM0fHxzb3ZlcmVpZ250eSUyMGRhbmlzaHxlbnwwfHx8fDE3NTE1ODQwOTB8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Digital sovereignty"><p>- s&#xE5; &quot;smukt&quot; formulerer en AI (ansat hos Google) digital suver&#xE6;nitet; p&#xE6;nt d&#xE6;kkende skulle jeg mene, men samtidig ogs&#xE5; 100% varm luft; s&#xE5; jeg t&#xE6;nker her at (pr&#xF8;ve) at lukke luften ud, og se om det er muligt at <em>materialisere</em> det floromvundne.</p><h3 id="ipindflydelses-perimeter">IP - Indflydelses Perimeter</h3><p>Gr&#xE6;nsen for, hvilke data og hvormeget, jeg kan kontrollere dem, er p&#xE5; samme tid b&#xE5;de flyttet t&#xE6;ttere p&#xE5;, og rykket l&#xE6;ngere v&#xE6;k! F&#xF8;r kommunereformen i 1970 kunne alle danskere g&#xE5;r til deres sogner&#xE5;dsformand og &quot;bede om godt vejr&quot; = blive &quot;lignet&quot; lempeligt i skat. Der er mange grunde til at staten gerne s&#xE5; ligningssystemet centraliseret, men mindst &#xE9;n af grundene var, at n&#xE6;rhedsprincippet var voldsomt udfordret af de personlige relationer &#x2013; s&#xE5; har jeg vist ikke sagt for meget &#x1F60E; <br>P&#xE5; det punkt er gr&#xE6;nsen rykket super t&#xE6;t p&#xE5; mig; i dag kan jeg n&#xE6;sten n&#xF8;jes med at skrive under (digitalt) og s&#xE5; er den selvangivelse afleveret. Da jeg startede med at programmere, var gr&#xE6;nsen for, hvad jeg kunne kontrollere, n&#xE6;rmest p&#xE5; l&#xE5;rene af mig. I dag kan jeg teoretisk set kontrollere data uanset hvor i verden de m&#xE5;tte befinde sig, fysisk. </p><p>Samtidig var jeg i dag n&#xF8;dt til at forklare en kunde, at deres &#xF8;nske om at sende billeder, direkte fra produktionen, med deres iPhone/iPad, til leverand&#xF8;rer rundt om i verden, via email og iMessage, var lidt rigeligt ambiti&#xF8;st, is&#xE6;r n&#xE5;r et voksende antal virksomheder helt ureflekteret brutalt fl&#xE5;r alle vedh&#xE6;ftninger af emails, og fascistoide IT driftsansvarlige fra deres <a href="https://www.google.com/search?q=what+are+mobile+management+system&amp;sca_esv=422b56c65b0d1f3c&amp;rlz=1C5CHFA_enDK952DK952&amp;sxsrf=AE3TifPYGejFoG5UW6FjrEwmo2ai6qbD2g%3A1751578603013&amp;ei=6_dmaINK4cDA8A_LuanwBg&amp;ved=0ahUKEwjD67bj0qGOAxVhIBAIHctcCm4Q4dUDCBA&amp;uact=5&amp;oq=what+are+mobile+management+system&amp;gs_lp=Egxnd3Mtd2l6LXNlcnAiIXdoYXQgYXJlIG1vYmlsZSBtYW5hZ2VtZW50IHN5c3RlbTIGEAAYFhgeMgUQABjvBTIFEAAY7wUyBRAAGO8FMggQABiABBiiBDIFEAAY7wVItJEBUI5cWN2KAXABeAGQAQCYAV2gAaUUqgECMzO4AQPIAQD4AQGYAiKgAtUVwgIKEAAYsAMY1gQYR8ICBBAjGCfCAg4QLhiABBixAxiDARiKBcICCxAuGIAEGLEDGIMBwgIIEAAYgAQYsQPCAg4QLhiABBixAxjRAxjHAcICChAjGIAEGCcYigXCAgsQLhiABBixAxjUAsICCxAAGIAEGLEDGIoFwgIFEAAYgATCAggQLhiABBixA8ICChAAGIAEGEMYigXCAgQQABgDwgIIEAAYgAQYywHCAgsQLhiABBjUAhjLAcICBRAuGIAEwgIHEAAYqQYYHsICCBAAGBYYChgemAMA4gMFEgExICmIBgGQBgiSBwQzMy4xoAe2hQKyBwQzMi4xuAfNFcIHBDItMzTIB5MB&amp;sclient=gws-wiz-serp&amp;ref=cunnin.gs" rel="noreferrer">MDM</a> v&#xE6;rkt&#xF8;j (jeg vil g&#xE6;tte p&#xE5; at en r&#xE6;kke produkter af samme kategori og funtionalitet, m&#xE5; kategoriseres under &quot;<a href="https://www.google.com/search?q=er+v%C3%A6rkt%C3%B8%C3%B8j+ut%C3%A6llelig&amp;sca_esv=5da2b0c0536aa051&amp;rlz=1C5CHFA_enDK952DK952&amp;biw=1470&amp;bih=740&amp;sxsrf=AE3TifPst-0erB3floohK-cy3LSuqf816w%3A1751579000176&amp;ei=ePlmaIS2Cvj_wPAPuqDP-Q0&amp;ved=0ahUKEwiE0ueg1KGOAxX4PxAIHTrQM98Q4dUDCBA&amp;uact=5&amp;oq=er+v%C3%A6rkt%C3%B8%C3%B8j+ut%C3%A6llelig&amp;gs_lp=Egxnd3Mtd2l6LXNlcnAiGWVyIHbDpnJrdMO4w7hqIHV0w6ZsbGVsaWcyBxAhGKABGApIllNQvSVY801wAXgBkAEAmAF2oAGBDqoBBDE2LjW4AQPIAQD4AQGYAhagAu4OwgIKEAAYsAMY1gQYR8ICChAjGIAEGCcYigXCAgQQIxgnwgILEAAYgAQYsQMYgwHCAg4QLhiABBixAxjRAxjHAcICBBAAGAPCAg4QABiABBixAxiDARiKBcICBRAuGIAEwgILEC4YgAQYxwEYrwHCAggQABiABBixA8ICBRAAGIAEwgIIEC4YgAQYsQPCAgsQLhiABBixAxiKBcICCBAuGIAEGNQCwgIHEAAYqQYYHsICBhAAGBYYHsICBxAAGIAEGA3CAgYQABgNGB7CAggQABgIGA0YHsICCBAAGKIEGIkFwgIIEAAYgAQYogTCAgUQABjvBZgDAOIDBRIBMSApiAYBkAYIkgcEMTYuNqAHn4MBsgcEMTUuNrgH5g7CBwgwLjguMTMuMcgHUA&amp;sclient=gws-wiz-serp&amp;ref=cunnin.gs" rel="noreferrer">ut&#xE6;llelige</a>&quot; &#x2013; men sp&#xF8;rg hellere <a href="https://www.linkedin.com/in/sophie-egede-schr%C3%B8der-3097145/?ref=cunnin.gs" rel="noreferrer">Sophie Egede-Schr&#xF8;der</a>) arm- og benamputerer kollegaers fors&#xF8;g p&#xE5; at finde en <em>work-life balance</em> midt i bleskift, kundesupport, og personlig udvikling. </p><p>Infrastrukturen har det alts&#xE5; vanskeligt - og det m&#xE6;rkes, n&#xE5;r man som jeg gik fra at kaste en aldrende MB 200D h&#xE5;rdt rundt i svingene i Odense i et tidligere &#xE5;rtusinde, i et fors&#xF8;g p&#xE5; at aflevere et brev (indeholdende min selvangivelse) personligt p&#xE5; skattekontorets parkeringsplads p&#xE5; fristnatten lidt i 23:59, til i starten af det friske &#xE5;rtusinde, uden v&#xE6;sentlige svedskjolder at kunne rykke store dele af M$&apos;s dokumentationsbibliotek ned fra hylden i Seattle (ganske vist over en nat, men alligevel). Hastigheden fejler med gigabit fiberforbindelser her i 2025 intet, men i konkurrencen med de <a href="https://www.thalesgroup.com/en/worldwide/digital-identity-and-security/magazine/bad-bots-rise-internet-traffic-hits-record-levels?ref=cunnin.gs" rel="noreferrer">over 50% af den samlede IP trafik</a>, som enten er af NSFW karakter, andet video/underholdningsindhold eller direkte har ilde hensigter, er min suver&#xE6;nitet begr&#xE6;nset til mit eget hjem (og det er i bedste fald).</p><p>Hvorvidt jeg kan kontrollere infrastrukturen og indirekte de anvendte teknologier i mit eget hjem kan der v&#xE6;re delte meninger om. Sp&#xF8;rger du min hustru vil svaret n&#xE6;ppe imponere. Uanset evner og ambitioner, er situationen imidlertid den, at jeg er i f&#xE6;rd med at &quot;migrere&quot; fra et 25 &#xE5;r gammelt, kuldsejlet, IHC projekt som aldrig rigtig fik flyveh&#xF8;jde &#x2013; til i dag med ZigBee, WiFi6 (802.11ax), Home Assistant, og produkter fra bla IKEA, faktisk at kunne realisere dr&#xF8;mmen om at t&#xE6;nde lyset hele vejen ud p&#xE5; toilettet om natten &#x2013;  automatisk, forst&#xE5;s &#x1F605;</p><p>Mine data tager nogle gevaldige t&#xE6;v, dagligt. Mit iWatch afleverer mit blodtryk, hjerterytme, og alt muligt andet, til hvem ved hvilken &quot;harddisk&quot;, i en ikke n&#xE6;rmere angivet region af - ja, gad vide hvor, men sikkert ikke (kun) de 45.000kvm i Foulum ved Viborg. Dertil naturligvis r&#xE5; m&#xE6;ngder af m&#xE5;lepunkter fra min g&#xF8;ren og laden p&#xE5; LinkedIn, krydset, Fj&#xE6;sen, Insta, foruden Discord, Slack, og s&#xE5; har jeg sikkert glemt et par stykker. Min holdning er at hvis nu jeg sammen med 1-200mio andre ivrige brugere g&#xE5;r 110% balalejka, s&#xE5; drukner vi big-tech&apos;erne i deres egne datas&#xF8;er &#x2013; s&#xE5; kan de l&#xE6;re det! Da jeg var barn, handlede det om at sidde i bestyrelsen for frysehuset, den lokale brugs, borgerforeningen, og ellers huske at m&#xF8;de op, n&#xE5;r der blev inviteret til kaffeslapperas rundt i de kropsdampfugtige ut&#xE6;tte <em>&#xE6; p&#xE6;n&apos; stouw</em>, langs landevejen fra stationsbyen til havet &#x2013; for her var SoMe gerne forsamlet 3-4 gange i l&#xF8;bet af ugen, samt naturligvis f&#xF8;r under og efter indk&#xF8;b hos k&#xF8;bmanden, og kirkegangen ikke at forglemme. Jeg kunne trygt lade stearinlysene br&#xE6;nde, hver tirsdag, n&#xE5;r jeg k&#xF8;rte til spejder - alle vidste, hvor jeg var og hvis noget br&#xE6;ndte, vidste de ogs&#xE5;, hvor de fik fat p&#xE5; mig. Nu ved Mark Zuckerberg og et par af hans h&#xE5;ndgangne m&#xE6;nd(-inder) det ogs&#xE5; - og det er m&#xE5;ske lige s&#xE5; meget i kontrol, som f&#xF8;r. </p><p>N&#xE5;r jeg, sammen med min grandf&#xE6;tter, i en alt for tidlig alder k&#xF8;bte cigaretter hos k&#xF8;bmanden, fik jeg en formaning med; den stod ikke p&#xE5; pakken, men kom fra k&#xF8;bmanden selv, der pulsende p&#xE5; en Manne, lige skulle h&#xF8;re, om jeg ikke troede at det var bedre at lade v&#xE6;re &#x2013; og jeg kunne v&#xE6;re helt sikker p&#xE5;, at alle der skulle, eller burde vide, hvad jeg havde v&#xE6;ret i gang med, var velinformeret, n&#xE6;sten f&#xF8;r jeg var hjemme igen! Sogner&#xE5;det (og dermed store dele af sognet) kendte min privat&#xF8;konomi, m&#xE5;ske bedre end jeg selv, og L&#xE6;kre Lotte, der ikke var karrig med ret meget, var det heller ikke med at dele mine &quot;tilb&#xF8;jeligheder&quot; hos fris&#xF8;ren. Nu deler vi selv materialet i vore <em>feeds</em> &#x2013; same but different. </p><p>S&#xE5; n&#xE5;r jeg spekulerer over, hvad min digitale sk&#xE6;bne er, hvad mit lod er, m&#xE5; det nok f&#xF8;rst og fremmest v&#xE6;re, en erkendelse af ikke at v&#xE6;re i kontrol og handle derefter. N&#xE5;r jeg kender hensigten med at overd&#xE6;nge mit tiltagende konsum af korte video stumper med stadig vildere tryllekunster, forvandlinger af skrotbunker til magel&#xF8;se boliger, opfindelser der dagligt frelser verden 11 gange, horder af nyudsprungne sangere og performere den ene dygtigere end den forrige, velkrydret med sponsorerede indslag med alt fra gardinopstrammere til elbiler, fikse plastikindretninger jeg ikke kan lave mad uden, og ikke mindst, et svimlende antal digitale l&#xF8;sninger, der med 3 hurtige klik med musen laver mit regnskab, redder min rapportering af mit overforbrug af el, vand, plastik, varme, kaffe, alt - s&#xE5; kan jeg handle derefter, og trygt h&#xE6;lde en frisk portion HavreFras blandet med Guldkorn op, sp&#xE6;de den grundigt op med dark Cocio og Tvis&apos; s&#xF8;dm&#xE6;lk, for &quot;de&quot; har altid gerne m&#xE5;ttet snage i mine &quot;data&quot;, s&#xE5;l&#xE6;nge jeg selv f&#xE5;r lov at f&#xF8;de dem; mine data er ren og forj&#xE6;ttende open source &#x2013; have a swing at it!</p><p></p>]]></content:encoded></item><item><title><![CDATA[Software 3.0]]></title><description><![CDATA[<p>Jeg ville &#xF8;nske at jeg kunne tage &#xE6;ren for at have m&#xF8;ntet udtrykket &#x2013; men det kan jeg ikke. Om det er Andrej Karpathy ved jeg ikke, men i <a href="https://youtu.be/LCEmiRjPEtQ?si=8_Du95Yi1ezMRl4X&amp;ref=cunnin.gs" rel="noreferrer">den her youtube</a> bruger han det i det mindste overordentligt effektivt!</p><p>Hvorfor er (endnu) en versionering af</p>]]></description><link>https://cunnin.gs/software-3-0/</link><guid isPermaLink="false">685b8a53029b6e0007775da7</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Wed, 25 Jun 2025 07:34:52 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1638496959438-502ee8a27a8d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEwfHxjb250cm9sfGVufDB8fHx8MTc1MDgzMDk3NXww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1638496959438-502ee8a27a8d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEwfHxjb250cm9sfGVufDB8fHx8MTc1MDgzMDk3NXww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Software 3.0"><p>Jeg ville &#xF8;nske at jeg kunne tage &#xE6;ren for at have m&#xF8;ntet udtrykket &#x2013; men det kan jeg ikke. Om det er Andrej Karpathy ved jeg ikke, men i <a href="https://youtu.be/LCEmiRjPEtQ?si=8_Du95Yi1ezMRl4X&amp;ref=cunnin.gs" rel="noreferrer">den her youtube</a> bruger han det i det mindste overordentligt effektivt!</p><p>Hvorfor er (endnu) en versionering af et begreb relevant? Jeg skal pr&#xF8;ve at gengive en af videoens centrale indsigter:</p><p>Andrej deler den indsigt at software i starten var instruktioner, der kunne styre en computer, som s&#xE5; til geng&#xE6;ld fx kunne anvise den korrekte bane for et objekt, hvis man &#xF8;nskede at dette objekt ramte et andet. Det kalder Andrej software 1.0. Det var den m&#xE5;de, hvorp&#xE5; vi var i stand til at interagere med en computer &#x2013; i stand til at f&#xE5; computeren til at udf&#xF8;re &quot;arbejde&quot; for os. </p><p>Software 2.0 er, for ham, ikke l&#xE6;ngere instruktioner, men ( k&#xE6;mpe datas&#xE6;t af matricer af) v&#xE6;gte [ v&#xE6;rdier mellem 0 og 1 ], der kan &quot;styre&quot; et neuralt netv&#xE6;rk til at udf&#xF8;re det samme arbejde, som ovenfor under software 1.0. Vores interaktion med computeren har flyttet sig milevidt fra de f&#xF8;rste simple instruktioner, og hvor vi tidligere m&#xE5;tte &quot;f&#xF8;de&quot; computeren med stakke af papkort og senere diske igennem s&#xE6;rligt indrettede indl&#xE6;sningsveje, l&#xE6;rer computeren nu vores hensigt ad mere menneskelignende indl&#xE6;sningsveje; mikrofon og kamera.</p><p>Med software 3.0 har vi n&#xE5;et et abstraktionsniveau i vores interaktion med computere, hvor vi ikke l&#xE6;ngere (n&#xF8;dvendigvis) skal fort&#xE6;lle computeren, hvordan vi &#xF8;nsker at den skal opf&#xF8;re sig (for at sikre at et objekt fx rammer et andet &#x2013; det kunne v&#xE6;re en bil i en rundk&#xF8;rsel), men hvad vi &#xF8;nsker at opn&#xE5;; at ramme den tredje udk&#xF8;rsel fra rundk&#xF8;rslen! Vores dialog med computeren har n&#xE5;et et abstraktionsniveau, hvor det (indtil videre i helt afgr&#xE6;nsede og klart definerede dom&#xE6;ner) lige s&#xE5; godt kunne v&#xE6;re en [menneske]kollega, vi taler med.</p><h3 id="konsekvensperspektiv">Konsekvens/perspektiv</h3><p>Der er vidtr&#xE6;kkende perspektiver i denne indsigt. Her er blot 3 af de mest umiddelbare:</p><p><strong>Tilpasning af software</strong><br>Det vil ikke v&#xE6;re relevant at tale om &quot;standard software&quot; eller om kunde tilpasninger til standard l&#xF8;sninger! Al <em>software</em> vil v&#xE6;re standard, fordi brugeren igennem sin interaktion definerer l&#xF8;sningen. L&#xE6;s eventuelt s&#xE6;tningen &#xE9;n gang til &#x1F609; og t&#xE6;nk s&#xE5; p&#xE5; de mange situationer, hvor det vil &#xE6;ndre din hverdag -</p><ul><li>&quot;bogf&#xF8;r fakturaen p&#xE5; kortfristet g&#xE6;ld, &#xE5;bne kreditor poster, indg&#xE5;ende moms, varek&#xF8;b, og &#xF8;g beholdningen af antal p&#xE5; lager for hvert produkt n&#xE6;vnt p&#xE5; fakturaen, og m&#xE6;rk linje 1 og 2 som bestilt til ordre. G&#xF8;r det for alle de fakturaer vi f&#xE5;r. Giv mig en notifikation, n&#xE5;r det er gjort, s&#xE5; jeg kan godkende arbejdet.&quot; <br>Det vil givetvis aff&#xF8;de en mindre byge af sp&#xF8;rgsm&#xE5;l/forslag fra <em>agenten</em>, men n&#xE5;r de er afklaret, og begreber som &quot;kortfristet g&#xE6;ld&quot; og &quot;indg&#xE5;ende moms&quot; er indl&#xE6;rt &#x2013; s&#xE5; vil der ikke v&#xE6;re brug for kontering l&#xE6;ngere. Det &quot;arbejde&quot; skal kun g&#xF8;res &#xE9;n gang, og s&#xE5; kan <strong>alle</strong> nyde godt af det!</li><li>&quot;pr&#xE6;sent&#xE9;r dig som kontorets receptionist, og sp&#xF8;rg hvad henvendelsen drejer sig om. Du kan viderestille alle henvendelser til John, medmindre det drejer sig om de her emner. Salg skal du viderestille til Jane. Reklamationer skal du viderestille til Erik. I n&#xE6;ste uge har vi efter&#xE5;rsferie og der beder du om at man enten ringer igen eller sender en email. Er opkaldet fra en agent som dig selv, m&#xE5; du gerne stille om til <a href="https://www.linkedin.com/posts/eordax_ai-mcp-agenticai-activity-7341932098129158145-yXFa?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAAEYICABKx84nM1IeHdZz0Y7ZykFfeGKQv0" rel="noreferrer">MCP</a>.&quot;<br>Digitale omstillingsborde (s&#xE5;kaldte PBX eller Private Branch Exchange) har v&#xE6;ret v&#xE6;ldigt popul&#xE6;re, ikke mindst siden hovedparten af al telekommunikation (l&#xE6;s: telefonsamtaler) er blevet flyttet over p&#xE5; VoIP (Voice over IP), men deres anvendelse og optimale udnyttelse har v&#xE6;ret voldsomt hindret af vanskelige brugergr&#xE6;nseflader; det vil v&#xE6;re historie lige om lidt!</li><li>&quot;L&#xE6;r reglerne om v&#xE6;rdians&#xE6;ttelse af ejendomme i den vedh&#xE6;ftede tekst, og gennemse derefter de domme, der i de seneste 10 &#xE5;r har v&#xE6;ret, hvor s&#xF8;gsm&#xE5;let har v&#xE6;ret relateret til uenighed om korrektheden og rimeligheden af den tilm&#xE5;lte v&#xE6;rdians&#xE6;ttelse. Foretag derefter en v&#xE6;rdians&#xE6;ttelse af alle ejendomme i Danmark. F&#xF8;rst for den enkelte matrikel, brug derefter v&#xE6;rdien for matriklen til at udregne et gennemsnit for alle matrikler p&#xE5; hver vej, brug derefter disse gennemsnit til at udregne et gennemsnit for alle veje i hvert kvart&#xE9;r. Send mig en notifikation n&#xE5;r du er f&#xE6;rdig&quot;<br>S&#xE5;dan bruger man 4 milliarder &#x1F60E; Jeg er helt p&#xE5; det rene med, at agenten ikke er gratis, og at systemet Danmark bruger i dag, ikke <em>kun</em> udregner en v&#xE6;rdi, men samtidig st&#xF8;tter de ansatte i dialogen med ejerne/&quot;kunderne&quot; &#x2013; m&#xE6;&#xE6;&#xE6;n</li></ul><p><strong>Gentrificering</strong><br>Adgangen til vand er afg&#xF8;rende for en bonde i Afrika. En br&#xF8;nd - uanset at han deler den med mange andre - g&#xF8;r det muligt for ham at v&#xE6;re en bedre bonde, dyrke flere afgr&#xF8;der, f&#xE5; bedre r&#xE5;d, og endelig &#xF8;ge hele omr&#xE5;dets v&#xE6;rdi og lokke flere/andre mere velst&#xE5;ende til omr&#xE5;det. Pr&#xE6;cis den samme udvikling s&#xE5;s igennem de seneste 40-50 &#xE5;r i hele omr&#xE5;det <a href="Gentrification of San Francisco  Wikipedia https://en.wikipedia.org &#x203A; wiki" rel="noreferrer">syd for San Francisco</a>, i dag omtales det som Silicon Valley. </p><p>Konsekvensen er &#xF8;gede indkomster, h&#xF8;jere priser p&#xE5; ejendom og varer, og h&#xF8;jere skatter, et rigere samfund med bedre r&#xE5;d til at hj&#xE6;lpe de, der ikke kan selv.</p><p><strong>&#xD8;konomisk &apos;revolution&apos;</strong><br>Det sidste perspektiv er af en lidt mere spekulativ karakter, omend hverken urealistisk eller s&#xE6;rligt originalt &#x2013; men stadig; spekulativt!<br>Den herskende &#xF8;konomiske model, som har bevist sig v&#xE6;rd igennem alle slags pr&#xF8;velser, siden <a href="https://samfundogpolitik.dk/adam-smith/?ref=cunnin.gs" rel="noreferrer">Adam Smith</a>&apos;s usynlige h&#xE5;nd, baserer sig p&#xE5; udbud og eftersp&#xF8;rgsel. If&#xF8;lge den vil udbuddet rette sig efter eftersp&#xF8;rgslen og vil man &#xE6;ndre p&#xE5; det, m&#xE5; man &#xE6;ndre eftersp&#xF8;rgslen. De seneste &#xE5;rtier har illustreret, hvordan evnen til at forme eftersp&#xF8;rgslen, har drevet udbuddet af alt fra surdej til &quot;plastiske forbedringer af legemsdele&quot;. <br>I hver fase af markeds&#xF8;konomiens udvikling, har det v&#xE6;ret <em>en knap ressource</em>, der har haft betydning for, hvordan og hvilke konkurrenter i et marked, der er lykkedes. De klassiske 3 M&apos;er &#x2013; Materialer, Mandkraft, og Moneter. For 30-40 &#xE5;r siden begyndte verden at flytte fremstillingsprocessen til Kina/&#xD8;sten - fordi der var adgangen til 2 af de 3 m&apos;er billigst (sikkert velvilligt hjulpet af det kinesiske kommunistparti, der givetvis spillede det lange spil).<br>Med automatiseringer og <em>built-to-order</em> af alt fra middagsretter til raketmotorer, fjernes &#xD8;stens konkurrencefordel p&#xE5; i det mindste det ene &apos;m&apos; &#x2013; og en r&#xE6;kke faktorer vil sikkert fjerne det andet &apos;m&apos; ogs&#xE5; (&#xF8;kologi, jord-til-bord, milj&#xF8;hensyn, social ansvarlighed, cirkul&#xE6;r &#xF8;konomi, mm). <br>Bev&#xE6;ger samfundet sig tilbage mod et &quot;h&#xE5;ndv&#xE6;rksbaseret&quot; punkt, hvor masseproduktionen ikke kan konkurrere. Vil <a href="https://insidesources.com/are-we-entering-the-age-of-the-individual/?ref=cunnin.gs#:~:text=The%20Age%20of%20the%20Individual%20will%20go,at%20a%20pace%20appropriate%20for%20an%20individual." rel="noreferrer">individdets tidsalder</a> skabe fundamentet under en ny &#xF8;konomisk revolution, hvor din indsats m&#xE5;ske stadig er afg&#xF8;rende for, hvor &quot;velst&#xE5;ende&quot; du er, men ikke for din deltagelse i samfundet eller din overlevelse. Vi er i dag langt t&#xE6;ttere p&#xE5; at kunne realisere dr&#xF8;mmen om <a href="https://www.britannica.com/money/universal-basic-income-ubi?ref=cunnin.gs" rel="noreferrer">Universal Basic Income</a> end p&#xE5; noget tidligere tidspunkt, men m&#xE5;ske er vi ogs&#xE5; n&#xF8;dt til det p&#xE5; grund af den koncentration af magt, velstand, og indflydelse som vi har v&#xE6;ret vidne til de seneste 10-15 &#xE5;r?!</p><p>Udnyttelsen af software 3.0 &#x2013; denne &quot;nye&quot; (industrielle) revolution &#x2013; har s&#xE5; mange konsekvenser for det danske samfund (alle samfund i verden, i realiteten) at det ikke er muligt at sammenfatte dem, men forestil dig rent afslutningsvis, at du stod i <a href="https://en.wikipedia.org/wiki/James_Watt?ref=cunnin.gs" rel="noreferrer">James Watt</a>&apos;s garage i midten af 1700-tallet og betragtede hans arbejde med dampmaskinen! Du havde med garanti ikke kunnet forestille dig, hvad det i l&#xF8;bet af ganske f&#xE5; &#xE5;rtier <a href="https://www.google.com/search?q=what+consequences+did+the+invention+of+the+steam+engine+have&amp;rlz=1C5CHFA_enDK952DK952&amp;oq=what+consequences+did+the+invention+of+the+steam+engine+have&amp;gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIKCAEQABiABBiiBDIKCAIQABiABBiiBDIHCAMQABjvBdIBCTE3NDczajBqNKgCALACAA&amp;sourceid=chrome&amp;ie=UTF-8&amp;ref=cunnin.gs" rel="noreferrer">fik af betydning</a> for samfundet.</p>]]></content:encoded></item><item><title><![CDATA[Hovedhjørnestenen]]></title><description><![CDATA[<p>Den aktuelle dialog om digital suver&#xE6;nitet tog vel strengt taget en drejning af n&#xE6;rmest bibelske dimensioner, her til morgen - da jeg &#xE5;bnede min indbakke kunne jeg nemlig l&#xE6;se: <a href="https://www.version2.dk/artikel/mitid-er-afhaengig-af-usas-tech-giganter-vi-er-noedt-til-forberede-os-paa-adgangen-blive-fjernet-fra?ref=cunnin.gs" rel="noreferrer">MitID er afh&#xE6;ngig af USAs tech-giganter</a>. I mit hoved blev det til</p>]]></description><link>https://cunnin.gs/hovedhjornestenen/</link><guid isPermaLink="false">6858f7f1029b6e0007775c93</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Mon, 23 Jun 2025 09:01:23 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1712218849130-7a0d1dff70dc?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDc5fHxjb3JuZXJzdG9uZXxlbnwwfHx8fDE3NTA2Njg1MTV8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1712218849130-7a0d1dff70dc?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDc5fHxjb3JuZXJzdG9uZXxlbnwwfHx8fDE3NTA2Njg1MTV8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Hovedhj&#xF8;rnestenen"><p>Den aktuelle dialog om digital suver&#xE6;nitet tog vel strengt taget en drejning af n&#xE6;rmest bibelske dimensioner, her til morgen - da jeg &#xE5;bnede min indbakke kunne jeg nemlig l&#xE6;se: <a href="https://www.version2.dk/artikel/mitid-er-afhaengig-af-usas-tech-giganter-vi-er-noedt-til-forberede-os-paa-adgangen-blive-fjernet-fra?ref=cunnin.gs" rel="noreferrer">MitID er afh&#xE6;ngig af USAs tech-giganter</a>. I mit hoved blev det til at vi har <a href="https://da.bibelsite.com/matthew/21-42.htm?ref=cunnin.gs" rel="noreferrer">valgt den forkerte tro</a>.</p><p>TL;DR; der er behov for en &#xE5;ben kilde l&#xF8;sning til integration af 2 systemer (maskin-til-maskin) hvor godkendelse er et krav.</p><p>Tillid er en altoverskyggende kvalitet, i et samfund, s&#xE5;vel som i en &#xF8;konomisk transaktion mellem 2 parter, eller derhjemme i &#xE6;gteskabet (eller hvilket forhold I nu lever under). IRL (nydansk for i virkeligheden) bruger du din &quot;mavefornemmelse&quot;, n&#xE5;r du st&#xE5;r og overvejer, om den her kebab nu ogs&#xE5; er &quot;frisk&quot; &#x2013; der er mange overvejelser som vejer ind: sult, tidspunkt p&#xE5; d&#xF8;gnet, alternativer, mm. Denne &apos;fornemmelse&apos; styrkes i mange sammenh&#xE6;nge af biometriske artifakter, her is&#xE6;r 2: &#xF8;jenkontakten og h&#xE5;ndtrykket. Digitalt har du meget mindre at arbejde med. Du kan (og det er blevet s&#xE5; meget mere relevant de seneste &#xE5;r, desv&#xE6;rre) browse s&#xE6;lger for at se om der skulle v&#xE6;re spor som peger p&#xE5;, at det er et scam; et fors&#xF8;g p&#xE5; en erstatning for &#xF8;jenkontakten. Du forlader dig p&#xE5; de forsikringer, som udstyret kommer med, efter at have indledt dialogen med et <em>hand-shake</em>; en digital version af h&#xE5;ndtrykket, hvor s&#xE6;lgers maskine og din enes om 2 ting, at begge er den, de udgiver sig for at v&#xE6;re, og hvilken protokol dialogen skal f&#xF8;lge (en slags aftale om hvilket sprog, der skal tales).</p><p>MitID er det seneste bud p&#xE5;, hvor danskere skal h&#xE5;ndtere etableringen af dette krav/behov for tillid, digitalt. Vi taler her om essensen af kritisk infrastruktur, der er afh&#xE6;ngig af app-stores (der hvor app&apos;en kan hentes og installeres fra). Det er imidlertid ikke den eneste infrastruktur, som dette v&#xE6;rkt&#xF8;j er afh&#xE6;ngig af. Driften og udviklingen ligger i h&#xE6;nderne p&#xE5; en fransk virksomhed. Transporten af data sker p&#xE5; udstyr, der for en stor dels vedkommende er produceret i Kina (hvad er efterh&#xE5;nden ikke det?), med protokoller og styremekanismer, der p&#xE5; &#xF8;jeblikke kan dirigere al trafik omkring eksempelvis NSA, og som drives af et par h&#xE5;ndfulde virksomheder, her is&#xE6;r TDC (ejet af en australsk kapitalfond), Telia/Telenor/Norlys (st&#xE6;rke indbyrdes kapitalinteresser, men Norlys er ejet af 800.000 el-kunder - et andelsselskab), og 3 (overvejende HongKong baseret konglomerat).</p><p>I den &apos;suppe&apos; virker det lidt <em>Ulven kommer</em> agtig at skyde &quot;skylden&quot; p&#xE5; Apple og Google!?</p><p>Har vi teknologien og &quot;kr&#xE6;fterne&quot; til at sikre en reel digital suver&#xE6;nitet p&#xE5; dette punkt? B&#xE5;de ja og nej. </p><p>Ja; fordi etableringen af et statsligt ejet teleselskab er lykkedes &#xE9;n gang tidligere - i slutningen af 1800-tallet, fordi vi har st&#xE6;rke kr&#xE6;fter indenfor udviklingen af optisk transmission af data, fordi landets topografi tillader etablering af &quot;luftb&#xE5;ren&quot; kommunikation relativt omkostningsn&#xE5;digt, og fordi vi har et h&#xF8;jt niveau indenfor forskningen i, og anvendelsen af kryptografi.</p><p>Nej; fordi Danmark er en del af EU og derfor &quot;tvunget&quot; til at gennemf&#xF8;re en r&#xE6;kke EU direktiver og her s&#xE5;ledes eksempelvis eIDAS (910/2014) og det vil derfor v&#xE6;re kontraproduktivt at udvikle et s&#xE6;rskilt dansk v&#xE6;rkt&#xF8;j, og MitID er efter <a href="https://digst.dk/it-loesninger/eid-og-single-digital-gateway/brug-af-eid-i-offentlige-digitale-loesninger-paa-tvaers-af-eueoes/?ref=cunnin.gs" rel="noreferrer">Digitaliseringsstyrelsens udsagn</a> s&#xE5;ledes allerede i stand til at servicere borgere fra andre landes behov for at identificere sig med deres tilsvarende eID. EU har i &#xF8;vrigt udgivet 2 glimrende PDF&apos;er, som i meget overskuelige illustrationer giver dig et hurtigt overblik over <a href="https://ec.europa.eu/newsroom/dae/document.cfm?doc_id=53865&amp;ref=cunnin.gs" rel="noreferrer">hvordan du kommer igang</a>, og <a href="https://ec.europa.eu/newsroom/dae/document.cfm?doc_id=53854&amp;ref=cunnin.gs" rel="noreferrer">hvilke v&#xE6;rkt&#xF8;jer</a> der potentielt ligger i v&#xE6;rkt&#xF8;jskassen.</p><p>Men plejer du ikke at bj&#xE6;ffe &apos;open source&apos; ved enhver lejlighed? Det har denne post ikke meget af?!</p><p>MitID kan man mene meget om &#x2013; og det skal man helt sikkert ogs&#xE5;, men bortset fra et helt &#xE5;benlyst behov for en dyb integration til brugerens fortrukne browser, fungerer v&#xE6;rkt&#xF8;jet efter hensigten; til konsumenter. Der findes danske MitID-brokere (som kan tilbyde at l&#xF8;fte en del af opgaven med at integrere l&#xF8;sninger med behovet for authentication [godkendelse af at du er den du siger du er]). Maskine-til-maskine er en noget andet st&#xF8;rrelse.</p><p>Og her kommer s&#xE5; et par bj&#xE6;f! For <em>agentic behaviour</em> (opf&#xF8;rsel p&#xE5; dine vegne) vil uden al tvivl komme til at fylde for s&#xE5;vel private virksomheder som offentlige ditto, og med rette, for arbejdet med at udveksle data mellem systemer, baseret p&#xE5; at en medarbejder skal sidde og <em>h&#xE5;ndstyre</em> det, er sv&#xE6;r at forestille sig i 2025. IT industriens seneste fors&#xF8;g p&#xE5; at finde et f&#xE6;lles fodslaw omkring udveksling af data har udm&#xF8;ntet sig i MCP (ja, branchen er om nogen tynget af et overdrevent brug af TLA&apos;s [Three Letter Acronyms] &#x1F60E;), der lover at give AI (agenter) adgang til at bruge/fjernstyre et system og data &#x2013; og i den kontekst vil MitID i &#xF8;jeblikket v&#xE6;re en k&#xE6;p i hjulet (jeg underholder gerne med, hvordan en jysk bank har l&#xF8;st opgaven).</p><p>S&#xE5;, i min kikkert er der et tydeligt behov for at <a href="https://digst.dk/?ref=cunnin.gs" rel="noreferrer">Digitaliseringsstyrelsen</a> udbyder en opgave, der kunne hedde: Fremstilling af en &#xE5;ben kilde l&#xF8;sning til integration af 2 systemer (maskin-til-maskin) hvor godkendelse, der overholder 910/2014, og mere sn&#xE6;vert, MitID (eller dennes kommende afl&#xF8;ser), er et krav. S&#xE5; er det op til markedskr&#xE6;fterne at konkurrere om at levere den bedste og billigste drift.</p>]]></content:encoded></item><item><title><![CDATA[Hvordan udnytter vi open source?]]></title><description><![CDATA[<p>Ordet <strong>udnytte</strong> er m&#xE5;ske den f&#xF8;rste fodfejl! M&#xE5;ske skal vi slet ikke <strong>udnytte</strong> open source, men snarere anerkende <a href="https://cunnin.gs/open-that-source-please/" rel="noreferrer">modellen under/bagved</a> og <a href="https://cunnin.gs/hvad-er-open-source/" rel="noreferrer">v&#xE6;rdien af den</a> og derefter deltage i communities bag de open source produkter, vi gerne vil bruge. S&#xE5; i</p>]]></description><link>https://cunnin.gs/hvordan-udnytter-vi-open-source/</link><guid isPermaLink="false">683054ef029b6e0007775906</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Mon, 23 Jun 2025 06:20:56 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1498435999018-6803de1f1c1f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE4fHx1c2V8ZW58MHx8fHwxNzQ5NjMxMjczfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1498435999018-6803de1f1c1f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE4fHx1c2V8ZW58MHx8fHwxNzQ5NjMxMjczfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Hvordan udnytter vi open source?"><p>Ordet <strong>udnytte</strong> er m&#xE5;ske den f&#xF8;rste fodfejl! M&#xE5;ske skal vi slet ikke <strong>udnytte</strong> open source, men snarere anerkende <a href="https://cunnin.gs/open-that-source-please/" rel="noreferrer">modellen under/bagved</a> og <a href="https://cunnin.gs/hvad-er-open-source/" rel="noreferrer">v&#xE6;rdien af den</a> og derefter deltage i communities bag de open source produkter, vi gerne vil bruge. S&#xE5; i stedet for at betale en leverand&#xF8;r for at udvikle en l&#xF8;sning til vores specifikke organisation eller virksomhed, kan vi betale den samme leverand&#xF8;r det samme bel&#xF8;b, for at udvikle en l&#xF8;sning til &quot;alle&quot; &#x1F632;</p><p>Den holdning skal jeg nok blive popul&#xE6;r p&#xE5;! &#x1F60E;</p><p>Udviklingsmodellen b&#xF8;r dog ikke fortjene s&#xE5; meget modvind, som jeg forventer. Fordi:</p><ol><li>f&#xF8;rst skal dom&#xE6;net og scope&apos;t klarl&#xE6;gges. De traditionelle workshops og foranalyser skal afholdes og et system skal designes.</li><li>dern&#xE6;st kommer et exploratorisk trin som (m&#xE5;ske / m&#xE5;ske ikke) findes og eller allerede benyttes i dag. Her kortl&#xE6;gges systemets mulige open source komponenter. Hvilke der allerede findes. Hvilke nye systemet potentielt indeholder, og endelig hvilke komponenter, der er s&#xE5; specifikke, at de skal &quot;tilpasses&quot; til den konkrete brugergruppe. At det findes ved jeg &#x2013; men det er muligvis ikke &quot;slutkunde&quot; transparent &#x1F609;</li><li>dern&#xE6;st allokeres ressourcer i en forholdsm&#xE6;ssig st&#xF8;rrelse, til de valgte open source komponenters udvikling og vedligeholdelse.</li><li>sidel&#xF8;bende udvikles de nye komponenter, open source s&#xE5;vel som closed source. For at underst&#xF8;tte anvendelsen af open source bedst muligt, sikres det under reviews og revisioner at opdatering af &#xE5;bne lister over eksisterende og anvendte open source komponenter bliver foretaget.</li><li>endelig samles alle komponenterne og l&#xF8;sningen overg&#xE5;r til en driftsmodel</li></ol><p>Der er ikke anvendt f&#xE6;rre ressourcer, snarere tv&#xE6;rtom! System/integrations leverand&#xF8;ren har alts&#xE5; ikke (endnu) mistet nogen oms&#xE6;tning, men f&#xE5;et solgt det samme eller flere konsulent timer. </p><p>Nu bem&#xE6;rkede jeg parentetisk - endnu - ovenfor, fordi det m&#xE5; forventes at AI st&#xF8;tte kommer til at g&#xF8;re et endog meget m&#xE6;rkbart indhug i det mindste i pkt 2-4, men det er en trend som ikke skyldes open source! Open source vil muligvis speed&apos;e transformationen op, men den vil ikke isoleret set v&#xE6;re skyld i de fleste &#xE6;ndringer!</p><p>Der ligger ihvertfald &#xE9;n altoverskyggende hindring for at alle vil kunne acceptere denne model: </p><blockquote>n&#xE5;r du g&#xE5;r til en SKODA forhandler, m&#xE5; du indstille dig p&#xE5;, at de (sj&#xE6;ldent) s&#xE6;lger dig en Renault!</blockquote><p>Det betyder at foruds&#xE6;tningen for at tage hul p&#xE5; et nyt mindset omkring integration og udvikling af IT l&#xF8;sninger fordrer</p><ol><li>at k&#xF8;ber v&#xE6;lger en leverand&#xF8;r, der har tilkendegivet at ville arbejde p&#xE5; de n&#xE6;vnte vilk&#xE5;r og efter udviklingsmodellen</li><li>at IT branchen eller muligvis et uafh&#xE6;ngigt organ udarbejder og vedligeholder en oversigt over system leverand&#xF8;rer og deres performance &#x2013; en slags AirBnB for IT projekter</li><li>at bestyrelser i s&#xE5;vel offentlig som ogs&#xE5; meget gerne i privat regi driver denne omstilling, kommunikeret i deres forventninger til direktioner og ledelser &#x2013; med den motivation, at de (<em>for the greater good</em>) tjener aktion&#xE6;rernes interesser bedst ad den vej!</li></ol>]]></content:encoded></item><item><title><![CDATA[Hvad er open source værd?]]></title><description><![CDATA[<p><a href="https://www.version2.dk/artikel/slagsmaal-i-dansk-open-source-netvaerk-har-meget-lidt-med-den-frie-kode-goere?ref=cunnin.gs" rel="noreferrer">Debatten</a> om <a href="https://cunnin.gs/open-that-source-please/" rel="noreferrer">open source</a> demonstrerer hvor v&#xE6;rdiladet et begreb, der er tale om. Her er et par vinkler p&#xE5;, hvorfor det er et meget v&#xE6;rdiladet begreb:</p><h2 id="buzzword">buzzword</h2><p>Open source er ikke noget nyt og sp&#xE6;ndende, som f.eks AI. N&#xE5;r det</p>]]></description><link>https://cunnin.gs/hvad-er-open-source/</link><guid isPermaLink="false">68302e75029b6e00077757db</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Wed, 11 Jun 2025 20:17:13 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1512358958014-b651a7ee1773?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDExMXx8cHVyc2UlMjB3aXRoJTIwbW9uZXl8ZW58MHx8fHwxNzQ3OTk3NzY3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1512358958014-b651a7ee1773?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDExMXx8cHVyc2UlMjB3aXRoJTIwbW9uZXl8ZW58MHx8fHwxNzQ3OTk3NzY3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Hvad er open source v&#xE6;rd?"><p><a href="https://www.version2.dk/artikel/slagsmaal-i-dansk-open-source-netvaerk-har-meget-lidt-med-den-frie-kode-goere?ref=cunnin.gs" rel="noreferrer">Debatten</a> om <a href="https://cunnin.gs/open-that-source-please/" rel="noreferrer">open source</a> demonstrerer hvor v&#xE6;rdiladet et begreb, der er tale om. Her er et par vinkler p&#xE5;, hvorfor det er et meget v&#xE6;rdiladet begreb:</p><h2 id="buzzword">buzzword</h2><p>Open source er ikke noget nyt og sp&#xE6;ndende, som f.eks AI. N&#xE5;r det alligevel bliver et <a href="https://ordnet.dk/ddo/ordbog?query=buzzword&amp;ref=cunnin.gs" rel="noreferrer">buzzword</a> skyldes det markedspotentialet. Det er ikke s&#xE6;rskilt for open source, mere et grundvilk&#xE5;r. I mods&#xE6;tning til begreber som GDPR, NIS2, AI, CO2, og mange flere, er markedspotentialet imidlertid ikke en konsekvens af brugen af begrebet i en m&#xE5;lrettet markedsf&#xF8;ring, i f&#xF8;rste &quot;b&#xF8;lge&quot;. Potentialet ligger i de strukturelle konsekvenser, som et &#xF8;get fokus og interesse for open source har. Jeg fristes til at sige <em>pull</em> i stedet for <em>push</em>.</p><p>Uanset &#x2013; som buzzword f&#xE5;r begrebet med den &#xF8;gede fokus, ogs&#xE5; v&#xE6;rdi for de virksomheder, der gerne vil &quot;h&#xE6;gte sig p&#xE5;&quot;; lidt i stil med da <em>rap kulturen</em> blev mainstream og alle artister skulle &quot;tale&quot; ind over deres numre. Det er den &quot;anden b&#xF8;lge&quot;, den traditionelle m&#xE5;lrettede markedsf&#xF8;ring af systemer med et s&#xE6;rligt s&#xE6;t af funktionalitet, til en konkret brugergruppe, nu blot p&#xE5;kl&#xE6;bet etiketten &quot;open source&quot;, eller <em>ingen er nogensinde blevet fyret for at v&#xE6;lge IBM</em>.</p><h2 id="open-source-fortolket-som">Open source fortolket som</h2><h3 id="free-as-in-beer">free as in beer...</h3><p>Mange, m&#xE5;ske reelt set de fleste, g&#xE5;r sk&#xE6;vt af begrebet <em>open source</em> og kommer nemt til at l&#xE6;gge noget deri, som det slet ikke kan levere p&#xE5;. I udgangspunktet er det software kildekode, der er tilg&#xE6;ngelig, s&#xE5; du kan l&#xE6;se den, forst&#xE5; den, afteste den, og for den sags skyld reproducere den. Begrebet <em>open source</em> siger intet om i hvilket omfang, du m&#xE5; udnytte det, dvs enten i privat/hobby regi eller <em>for profit</em>. Det er strengt taget blot en &quot;etiket&quot;. Et eksempel er Google Chrome (browseren rigtig mange bruger p&#xE5; Internettet). Den er ikke open source, men gratis at hente. Det betegnes freeware. Det er der vittige hoveder som har ben&#xE6;vnt <em>free as in beer</em> &#x2013; alts&#xE5; gratis &#xF8;l. Du kan drikke det, men du f&#xE5;r ikke opskriften p&#xE5; det. </p><p>Hvad er v&#xE6;rdien af freeware? Hvem kan have en interesse i at udvikle meget avancerede programmer, for derefter at for&#xE6;re dem bort? Tidligere var der 2 grupper</p><ul><li>hackere som skulle bruge en vej ind p&#xE5; din computer</li><li>afpr&#xF8;vning af ny teknologi, hvor producenten, mod at du bruger det gratis, f&#xE5;r lov at indsamle data om brugen</li></ul><p>De fleste er nok klar over at gruppe 2 i dag har f&#xE5;et en altoverskyggende &quot;undergruppe&quot;, hvor det er dig, som bruger, der reelt er producenten af indholdet til programmet, og at programmets succes afh&#xE6;nger af at flest mulige &quot;producerer&quot; indhold. Gevinsten ligger i at udnytte &quot;programfladen&quot; til at sende reklamer med, eller udnytte dine vaner og pr&#xE6;ferencer, n&#xE5;r du bruger programmet, til at udregne, hvilke andre produkter og tjenester, du ville kunne finde p&#xE5; at k&#xF8;be, og derefter m&#xE5;lrette markedsf&#xF8;ring mod dig, for dem.</p><h3 id="free-as-in-speech">free as in speech...</h3><p>I mods&#xE6;tning til det st&#xE5;r &quot;&#xE6;gte&quot; open source, der typisk f&#xF8;lges af et tekstdokument med navnet LICENSE, hvor du som l&#xE6;ser bliver gjort opm&#xE6;rksom p&#xE5;, under hvilke betingelser, du m&#xE5; benytte kildekoden. Ofte anvendes GPL (General Public License) og den type software betegnes ogs&#xE5; mere specifikt som <em>free software</em> &#x2013; fordi du m&#xE5; g&#xF8;re med den hvad du lyster.</p><p>Chrome i eksemplet f&#xF8;r, er baseret p&#xE5; et open source project, Chromium, som f&#xF8;lges af en <a href="https://en.wikipedia.org/wiki/BSD_licenses?ref=cunnin.gs" rel="noreferrer">BSD-3</a> licens, som fastsl&#xE5;r at du kan bruge kildekoden, blot du</p><ul><li>husker at vedl&#xE6;gge copyright betegnelsen (typisk &#xE9;n linje der siger hvem der er forfatter til kildekoden) hvis du distribuerer kildekoden</li><li>husker at g&#xF8;re opm&#xE6;rksom p&#xE5; copyright (igen, hvem forfatteren er) hvis du distribuerer eksekverbart, dvs programmer, du kan starte</li><li>ikke bruger forfatterens navn/m&#xE6;rke til at markedsf&#xF8;re dit eget produkt </li></ul><h2 id="hvor-er-v%C3%A6rdien">Hvor er v&#xE6;rdien?</h2><p>Hvad er v&#xE6;rdien af open source? Skriver du en tekst og eller en melodi blot for at give den v&#xE6;k? Hugger en skulptur, anl&#xE6;gger en have, pudser naboens vinduer?</p><p>I mods&#xE6;tning til skulpturen, haven og vinduerne, har du ikke &quot;mistet&quot; noget. Du har ganske vist brugt noget tid, men hvis det var tid, du var indstillet p&#xE5; at bruge i alle tilf&#xE6;lde &#x2013; m&#xE5;ske fordi du skulle bruge funktionen eller programmet selv &#x2013; s&#xE5; er din gr&#xE6;nseomkostning (omkostningerne ved at &quot;producere&quot; &#xE9;n mere magen til) uendeligt lille, hvis ikke nul. S&#xE5; du har ikke mistet noget. </p><p>S&#xE5;, det at du &#xE5;bner et produkt og giver andre indsigt og adgang, risikerer du ikke noget ved, men har potentielt muligheden for at f&#xE5; et bedre produkt ved, hvis de, der v&#xE6;lger at bruge produktet, giver feedback, &#xE6;ndringsforslag, p&#xE5;peger fejl og sikkerhedsbrist. V&#xE6;rdien i open source er returl&#xF8;bet.</p><p>Der er en udbredt forestilling om at hvis noget er gratis, er det ikke noget v&#xE6;rd. I rigtig mange sammenh&#xE6;nge er det rigtigt (Trabant var et eksempel p&#xE5;, at du ganske enkelt ikke kan bygge en bil af &#xF8;ld&#xE5;ser), og isoleret set g&#xE6;lder det ogs&#xE5; software, men fordi (som jeg n&#xE6;vnte ovenfor) gr&#xE6;nseomkostningen ved at fremstille &#xE9;n kopi mere af et program &#x2013; i mods&#xE6;tning til at fremstille &#xE9;n bil mere &#x2013;  n&#xE6;rmest er nul, opst&#xE5;r nogle meget &quot;m&#xE6;rkelige&quot; markedsmekanismer, ganske vist <a href="https://www.investopedia.com/terms/c/ceterisparibus.asp?ref=cunnin.gs" rel="noreferrer">ceteris paribus</a> (alt andet lige)!</p><p>For, hvis omkostningen er nul, mister virksomheden ikke noget ved at lade endnu en bruger benytte kode, virksomheden allerede er blevet betalt for at skrive. L&#xE6;s lige s&#xE6;tningen igen, og fokus&#xE9;r p&#xE5; ordene &quot;allerede er blevet betalt for&quot; &#x2013; den kommer vi tilbage til!</p><p>Hvis nyttev&#xE6;rdien for den enkelte bruger er konstant, er v&#xE6;rdien af &quot;koden&quot; lig med summen af brugeres nyttev&#xE6;rdi, og hvis gr&#xE6;nseomkostningen er nul, vil overskuddet stige eksponentielt med hver ny bruger &#x2013; eller s&#xE5;dan var mantraet i det mindste i starten af &#xE5;rtusindet, og en del af forklaringen p&#xE5;, hvorfor Internet virksomheder blev solgt til astronomiske bel&#xF8;b; deres v&#xE6;rdians&#xE6;ttelse var baseret p&#xE5; den drifts&#xF8;konomiske kalkulation! Dertil kunne l&#xE6;gges den indirekte oms&#xE6;tning som en r&#xE6;kke af disse virksomheder &quot;s&#xE5;&quot; som en slags fl&#xF8;deskum p&#xE5; kagen: brugerens brug af tjenesten kunne g&#xF8;res til genstand for <em>monetization</em> (brugernes brug = transaktionsdata = et produkt i sig selv = salgbar).</p><p>Ceteris paribus er, som du m&#xE5;ske kan forestille dig, en noget illusorisk situation; for der er et hav af andre variable, som har indflydelse p&#xE5; prisdannelsen &#x2013; heriblandt det &#xE5;benlyse behov for infrastruktur! Internet b&#xE5;ndbredde,  processorkraft og datalagerplads i s&#xE6;rdeleshed. S&#xE5;, m&#xE5;ske er koden &quot;gratis&quot; for bruger 2 - &#x221E; men hver ny bruger &#xF8;ger kravene til infrastruktur. Anskaffelsen af kunder skulle ogs&#xE5; nyt&#xE6;nkes. Tidligere solgte produkter igennem magasiner, p&#xE5; messer, og mund-til-mund. I dag &quot;k&#xF8;bes&quot; kunder med k&#xE6;mpe reklamekampagner p&#xE5; alle &quot;platforme&quot; (is&#xE6;r de elektroniske og helt s&#xE6;rligt de sociale medier). L&#xE6;gger &apos;man&apos; de omkostninger til, er gr&#xE6;nseomkostningen ikke l&#xE6;ngere nul, men dog stadig forunderlig lille, fordi den sidste kunde ikke belaster <em>serverparken</em> eller <em>marketingbudgettet</em> med noget reelt m&#xE5;leligt tal!</p><p>Pr&#xF8;ver vi at anskueligg&#xF8;re det i et diagram, f&#xE5;r vi (i hele tr&#xE6;skol&#xE6;ngder) en graf, der nogenlunde ser s&#xE5;dan ud:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/06/Paper.Home.20.png" class="kg-image" alt="Hvad er open source v&#xE6;rd?" loading="lazy" width="2000" height="1500" srcset="https://cunnin.gs/content/images/size/w600/2025/06/Paper.Home.20.png 600w, https://cunnin.gs/content/images/size/w1000/2025/06/Paper.Home.20.png 1000w, https://cunnin.gs/content/images/size/w1600/2025/06/Paper.Home.20.png 1600w, https://cunnin.gs/content/images/2025/06/Paper.Home.20.png 2224w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">besynderlig gr&#xE6;nseomkostningskurve</span></figcaption></figure><p>Den f&#xF8;rste bruger koster en bondeg&#xE5;rd! Ikke infrastrukturen (hvilket er en anden besynderlighed, som vi kan komme ind p&#xE5; senere), men koden. Den er hundedyr at anskaffe. Dagsprisen for en udvikler i den spidse ende, i Chicago fx, ligger fra 250.000 US$ om &#xE5;ret, og de arbejder bedst i teams af minimum 2-3 stykker, og ikke mange timer om dagen (netto).</p><p>Den anden og om muligt endnu mere besynderlige kurve er gr&#xE6;nseindt&#xE6;gtskurven &#x2013; alts&#xE5; hvormeget den sidste kunde oms&#xE6;tter for (og her husker vi p&#xE5; at &apos;kunden&apos; ikke betaler [direkte] for produktet, men kun i kraft af sin g&#xF8;ren og laden &quot;i&quot; produktet, danner transaktionsdata, der kan udnyttes kommercielt. Den kurve har f&#xE5;et et navn: hockey-stick, fordi den starter og i lang tid er en ret linje (f&#xE5; brugeres data er ingenting v&#xE6;rd), men lige med et n&#xE5;r datam&#xE6;ngden et punkt, hvor dets evne til at sige noget har relevans (hvis 5 kunder k&#xF8;ber <em>salte fisk</em> fra Katjes er der ingen, der lytter, men hvis 500.000 g&#xF8;r det er historien en helt anden)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/06/Paper.Home.20-1.png" class="kg-image" alt="Hvad er open source v&#xE6;rd?" loading="lazy" width="2000" height="1500" srcset="https://cunnin.gs/content/images/size/w600/2025/06/Paper.Home.20-1.png 600w, https://cunnin.gs/content/images/size/w1000/2025/06/Paper.Home.20-1.png 1000w, https://cunnin.gs/content/images/size/w1600/2025/06/Paper.Home.20-1.png 1600w, https://cunnin.gs/content/images/2025/06/Paper.Home.20-1.png 2224w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">lige s&#xE5; besynderlig gr&#xE6;nseoms&#xE6;tningskurve</span></figcaption></figure><p>Tidligere bad jeg dig l&#xE6;se en s&#xE6;tning 2 gange. Den er essentiel for forst&#xE5;elsen af v&#xE6;rdians&#xE6;ttelsen af open source! Tyskerne siger: von nix kommt nix; vi siger f&#xF8;rst yde s&#xE5; nyde (uden at det helt er det samme), men engelsk har udtrykket <a href="https://en.wikipedia.org/wiki/No_such_thing_as_a_free_lunch?ref=cunnin.gs" rel="noreferrer">TINSTAAFL</a> (<em>there is no such thing as a free lunch)</em>. Den virksomhed, der &quot;s&#xE6;lger&quot; open source, g&#xF8;r det alts&#xE5; med en bagtanke (som heroin pusherens <em>first trip is on the house</em>) om at s&#xE6;lge bedre versioner af produktet med mere funktionalitet eller helt andre produkter, hvor open source produktet s&#xE5; m&#xE5;ske erstattes af closed source, eller, som det er tilf&#xE6;ldet med megen freeware, for at s&#xE6;lge brugerens transaktions- og metadata.</p><p>Noget software er tilg&#xE6;ngeligt af andre grunde, fx fordi selskabet bag var n&#xF8;dt til at lukke og opf&#xF8;rte sig anst&#xE6;ndigt i processen [ved at open source dets produkt(er)]. Producenten kan ogs&#xE5; v&#xE6;re en enkelt-person, der som en del-leverance i forbindelse med en eksamen eller anden uddannelsesopgave har skullet fremstille kode med en s&#xE6;rlig funktionalitet, som vedkommende efterf&#xF8;lgende har haft lyst til at dele [m&#xE5;ske dybest set fordi vedkommende kunne]. Der er ogs&#xE5; den altruistiske person/organisation, som alene med henblik p&#xE5; samfundets vel, bygger og udbyder software til fri afbenyttelse. F&#xE6;lles for de fleste af disse eksempler er imidlertid, at det kr&#xE6;ver, at du selv finder ud af at udnytte den software: installation, ops&#xE6;tning, drift, tilsyn og pasning, mm.</p><p>V&#xE6;rdians&#xE6;ttelsen af open source er alts&#xE5; ikke, som med eksemplet en bil, mulig at beregne ud fra hverken produktets omkostninger, eller dets nyttefunktion! Den kan i bedste fald sammenlignes med den m&#xE5;de hvorp&#xE5; s&#xE5;kaldte &quot;str&#xF8;varer&quot; eller brand merchandise (merch eller swag) v&#xE6;rdians&#xE6;ttes - og selv her er det kun en tiln&#xE6;rmelse.</p><h2 id="samfundsnytten">Samfundsnytten</h2><p>Med de briller og i den kontekst er det sv&#xE6;rt at se hvordan open source kan bidrage positivt; at det har nogen reel samfundsnytte! Jeg vil dog alligevel fors&#xF8;ge at tegne et billede, hvor open source har en funktion og en positiv samfundsnytte!</p><h3 id="digital-suver%C3%A6nitet">Digital suver&#xE6;nitet</h3><p>Hvad ingen sk&#xE6;nkede en tanke for blot 6 m&#xE5;neder siden, er blevet m&#xE5;ske d&#xE9;t st&#xF8;rste <em>selling point</em> for leverand&#xF8;rer af digitale produkter og tjenesteydelser. En forunderlig verden!</p><p>En hj&#xF8;rnesten i denne suver&#xE6;nitet er risiko afd&#xE6;kning; alts&#xE5; at brugeren uanset om det er en virksomhed, en organisation, eller et helt samfund, suver&#xE6;nt kan (for)sikre sig mod de risici, som anvendelsen af et digitalt produkt eller en tjenesteydelse (automatisk) introducerer. Skal denne afd&#xE6;kning v&#xE6;re effektiv, kr&#xE6;ver det at produktets funktion, dets processer, datastr&#xF8;mme, og -behandlinger kan revideres, efterpr&#xF8;ves, og reproduceres. Til det form&#xE5;l er open source &#x2013; alts&#xE5; adgangen til kildekoden &#x2013; essentiel!</p><h3 id="tilpasningsevne">Tilpasningsevne</h3><p>De samfundsstr&#xF8;mninger vi er vidner til, her i midten af &apos;20-erne, kr&#xE6;ver i endnu st&#xF8;rre omfang end for 10 &#xE5;r siden, at vi er omstillingsparate. For at udnytte nye teknologier, som eksempelvis LLM&apos;er (large language models) der er, i det mindste en v&#xE6;sentlig del af, grundlaget for AI, vil det kr&#xE6;ve at processer og arbejdsgange forandres i en takt og omfang, som ikke er set tidligere. Det vil ikke v&#xE6;re muligt med <em>closed source</em> alene af den grund at AI modeller ikke kan &quot;se&quot; ind i de sorte bokse, som <em>closed source</em> produkter reelt er. Leverand&#xF8;rer kan v&#xE6;lge at bygge <a href="https://modelcontextprotocol.io/introduction?ref=cunnin.gs" rel="noreferrer">MCP&apos;er</a> til deres propriet&#xE6;re systemer, men adgangen til kildekoden vil v&#xE6;re langt at foretr&#xE6;kke, fordi det vil s&#xE6;tte AI agenter i stand til at forbedre produktet i stedet for at skulle n&#xF8;jes med at arbejde videre med / uden om produktets data leverancer.</p><h3 id="skal%C3%A9rbarhed">Skal&#xE9;rbarhed</h3><p>Prisen p&#xE5; at udvikle software (p&#xE5; traditionel vis) stiger b&#xE5;de fordi kvalitetskravene stiger, men is&#xE6;r fordi produktiviteten falder (den f&#xE5;r jeg sikkert hug for, men s&#xE5;dan m&#xE5; det v&#xE6;re, n&#xE5;r man husker p&#xE5; at Simonyi og Brodie skrev Word, i &#xF8;vrigt anekdotisk p&#xE5; pizza og Jolt Coke [se dog <a href="https://bluegoatcyber.com/blog/do-hackers-really-drink-jolt-cola-the-truth-revealed/?ref=cunnin.gs" rel="noreferrer">her</a>], p&#xE5; mindre end 2 &#xE5;r i starten af &apos;80-erne, mens skat.dk vel er p&#xE5; mindst 10-ende &#xE5;r i det aktuelle &quot;rul&quot;, og helt sikkert har mere end 2 personer p&#xE5; opgaven) &#x2013; jeg finder selv ud...</p><p>Uanset; en omfattende &#xE6;ndring/omkalfatring af hele eller store dele af det kompleks af systemer, som kommuner, region og stat bruger, vil kr&#xE6;ve s&#xE5; enorme ressourcer, at en traditionel tilgang til opgaven ganske vil <em>sande til</em> (som det desv&#xE6;rre er set ske i, n&#xE5; ja, nu n&#xE6;vnte jeg Skat, men der er uheldigvis rigeligt af eksempler i hatten, og at det m&#xE5; v&#xE6;re s&#xE5;dan forklarer eksempelvis <a href="https://linearb.io/resources/software-engineering-benchmarks-report?ref=cunnin.gs" rel="noopener">LinearB</a>).</p><p>Sker udviklingen imidlertid p&#xE5; helt andre licensieringsvilk&#xE5;r, hvor leverand&#xF8;rerne ikke &quot;f&#xE5;r lov&quot; at tjene pengene mere end &#xE9;n gang, men m&#xE5; n&#xF8;jes med at tage sig betalt for at levere en open source, vil det v&#xE6;re muligt at skal&#xE9;re l&#xF8;sninger p&#xE5; tv&#xE6;rs af kommuner, kontorer og styrelser til ganske anderledes overskuelige omkostninger og ikke mindst tidsrammer (blandt andet&#xA0;fordi reproduktionen af l&#xF8;sningen kan ske parallelt i alle sammenh&#xE6;nge, med samme behov.</p><h3 id="kompleksiteten">Kompleksiteten</h3><p>Endelig vil open source kunne spille en afg&#xF8;rende rolle i en tiltr&#xE6;ngt og n&#xF8;dvendig reduktion af kompleksiteten.</p><p>Her tjener et eksempel m&#xE5;ske. I et t&#xE6;nkt kontor behandles ans&#xF8;gninger om tilladelse til at udvide byggeprocenten p&#xE5; matrikler. Dialogen med ans&#xF8;ger sker, af hensyn til regels&#xE6;t om &#xE5;benhed i den offentlige forvaltning, i et journaliseringssystem, der er bygget sammen med en unavngiven, omend s&#xE6;rdeles velkendt, kontorpakke, prim&#xE6;rt for at tilgodese brugernes <em>user experience</em> (brugeroplevelse). Opdateringer af kontorpakken, der i l&#xF8;sningens levetid de f&#xF8;rste par gange medf&#xF8;rte f&#xF8;rst mindre men senere mere omfattende &#xE6;ndringer/tilretninger i gr&#xE6;nsesnittet og brugerfladen i journaliseringssystemet, er de seneste &#xE5;r blevet opgivet pga bekosteligheden i kravene til &#xE6;ndringer/tilretninger, for endeligt helt at blive gjort umulige, da afviklingen af kontorpakken blev flyttet &quot;i skyen&quot;. Kontoret &#xF8;nsker sig aktuelt et nyt journaliseringssystem, der kan integreres med kontorpakken i skyen.</p><p>Eksemplet er sikkert fuldst&#xE6;ndig hypotetisk og helt ude af trit med virkeligheden, men tjener ogs&#xE5; mest for at pege p&#xE5; et par pinde</p><ul><li>en digital mange-sidet dialog orienteret l&#xF8;sning, hvor den enkelte dialog p&#xE5; anfordring kan pr&#xE6;senteres i et s&#xE6;rskilt digitalt format (aktindsigt), b&#xF8;r v&#xE6;re lige s&#xE5; efterspurgt p&#xE5; ethvert kontor i st&#xF8;rstedelen af den offentlige sektor, som en regnemaskine p&#xE5; et revisionskontor (l&#xE6;s: noget der &#x201C;lugter&#x201D; langt v&#xE6;k af standard)</li><li>l&#xF8;sningen vil v&#xE6;re et oplagt &quot;offer&quot; for et utal af integrationer</li><li>fokus p&#xE5; det essentielle i l&#xF8;sningen risikerer p&#xE5; kort tid at blive afl&#xF8;st af en myriade af <em>feature-creep</em> (ogs&#xE5; ben&#xE6;vnt <a href="https://en.wikipedia.org/wiki/Feature_creep?ref=cunnin.gs" rel="noreferrer">scope-creep</a>) fordi systemets mange interessenter ser hver deres subjektive interesse i &quot;forbedringer&quot; &#x2013; eksempelvis at benytte en kontorpakke til at &quot;fremstille&quot; hhv pr&#xE6;sentere indholdet af dialogen (den faktiske ordlyd) i stedet for at &apos;n&#xF8;jes&apos; med f&#xE5; tekst formateringsmuligheder</li><li>den eneste m&#xE5;de en leverand&#xF8;r kan tilf&#xF8;re produktet merv&#xE6;rdi vil v&#xE6;re igennem udbygning af produktets scope, hvorfor det ad &#xE5;re nemt kommer til at l&#xF8;se alt fra OCR scanning til email (integration til kontorpakken vel at m&#xE6;rke &#x2013; for det er brugerne vant til og n&#xF8;dt til at have, til den interne kommunikation, og det vil jo v&#xE6;re ubrugeligt og h&#xE5;bl&#xF8;st tidsspilde, hvis der var flere systemer til den opgave) &#x2013; en passant: forestil dig en t&#xF8;mrer stolt vise dig deres nye hammer, der ogs&#xE5; kan bryde murv&#xE6;rk ned og stifte indramninger af billeder (fordi det jo ville v&#xE6;re tidsspilde at have flere v&#xE6;rkt&#xF8;jer, der kan sl&#xE5; s&#xF8;m i)</li></ul><p>Open source har meget at tilbyde her:</p><ol><li>en r&#xE6;kke dialog orienterede l&#xF8;sninger at starte p&#xE5;/med</li><li>&#xE5;ben kildekode, og typisk veldokumenterede api&apos;er, som vil lette behovet, ved etableringen af integrationer (n&#xE5;r dette er uafvendeligt)</li><li>feature-creep er et grundvilk&#xE5;r; der hj&#xE6;lper ingen k&#xE6;re mor (eller open source)</li><li>et afleveret open source system kan konkurrenceuds&#xE6;ttes i en helt anden grad end l&#xF8;sninger, hvor leverand&#xF8;ren st&#xE5;r for at skrive kravspecifikationen til det n&#xE6;ste &quot;ehhh offentlige&quot; udbud, hvor behovet for udbygning vil v&#xE6;re brugernes behov for &#xF8;get funktionalitet i st&#xF8;rre udstr&#xE6;kning, og leverand&#xF8;rernes behov for &#xF8;get oms&#xE6;tning i mindre udstr&#xE6;kning</li></ol>]]></content:encoded></item><item><title><![CDATA[Open (that) Source (please)]]></title><description><![CDATA[<p>OSS&#x200A;&#x2014;&#x200A;Open Source Software&#x200A;&#x2014;&#x200A;hvorfor taler alle pludselig om det? Fejlede den &#x201C;gamle&#x201D; <em>source</em> da noget? Er det endnu et i en uendelig r&#xE6;kke af buzz-words som IT branchen er s&#xE5; berygtet for at v&#xE6;re god til</p>]]></description><link>https://cunnin.gs/open-that-source-please/</link><guid isPermaLink="false">682f8b7d029b6e00077755f2</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Wed, 11 Jun 2025 06:04:06 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1599595344070-c456bea6ee98?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fG9wZW4lMjBzb3VyfGVufDB8fHx8MTc0Nzk5NjUwMHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1599595344070-c456bea6ee98?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fG9wZW4lMjBzb3VyfGVufDB8fHx8MTc0Nzk5NjUwMHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Open (that) Source (please)"><p>OSS&#x200A;&#x2014;&#x200A;Open Source Software&#x200A;&#x2014;&#x200A;hvorfor taler alle pludselig om det? Fejlede den &#x201C;gamle&#x201D; <em>source</em> da noget? Er det endnu et i en uendelig r&#xE6;kke af buzz-words som IT branchen er s&#xE5; berygtet for at v&#xE6;re god til at opfinde? M&#xE5;ske kan du bare n&#xF8;jes med at sp&#xF8;rge ChatGPT: why did open source happen? &#x1F609;</p><h2 id="antitesen">Antitesen</h2><p>For at forst&#xE5; begrebet er vi n&#xF8;dt til f&#xF8;rst at forst&#xE5; begrebets antitese: closed source.</p><p>For over 60 &#xE5;r siden byggede ingeni&#xF8;rer computere, og s&#xE5; &quot;byggede&quot; de et program (kode) der kunne k&#xF8;re p&#xE5; computeren. Helt specifikt til <strong>den</strong> computer. N&#xE5;r astrofysikere og national&#xF8;konomer skulle &quot;knuse store talm&#xE6;ngder&quot; tegnede de streger p&#xE5; k&#xE6;mpestakke af <a href="https://datamuseum.dk/wiki/Hulkort?ref=cunnin.gs" rel="noreferrer">papkort</a>, som s&#xE5; b&#xE5;de var program og data, og lod computeren l&#xE6;se dem, og s&#xE5; ventede alle i sp&#xE6;nding p&#xE5; resultatet, timer og dage senere. Det var overvejende stater som havde r&#xE5;d til at have en h&#xE6;r af ingeni&#xF8;rer g&#xE5;ende og bygge udstyret og programmerne (stakkene af papkort). Universiteterne var f&#xF8;rst til at forske i bygningen af udstyr og programmer. I de milj&#xF8;er deler man gerne &#x2013;  fordi forskere alle st&#xE5;r p&#xE5; skuldrene af andre forskeres arbejde; samarbejde g&#xF8;r st&#xE6;rk.</p><p>Der var virksomheder, som s&#xE5; muligheder i computerne &#x2013; og blev rigtig dygtige til at bygge maskinerne, og &quot;programmere&quot; dem (tegne streger p&#xE5; papkort). En af dem var IBM. Der blev snart h&#xE5;rd konkurrence i feltet, fordi perspektiverne var overv&#xE6;ldende, og kompetencerne blev konkurrencefordele, som man n&#xF8;digt delte med konkurrenterne. Konkurrenterne fors&#xF8;gte i stedet at lokke dygtige medarbejdere til sig i h&#xE5;bet om at de ad den vej kunne f&#xE5; adgang til kompetencerne. Modtr&#xE6;kket var patenter og <a href="https://www.wipo.int/en/web/about-ip?ref=cunnin.gs" rel="noreferrer">intellectual property</a> som tvang konkurrenter til at betale licens for at bruge en konkurrents design og metoder (selv om de m&#xE5;ske havde lokket &quot;opfinderen&quot; over til sig). S&#xE5;dan gik designet af udstyret og papkortene fra at v&#xE6;re noget man delte, til at v&#xE6;re <em>closed source</em>. I takt med at virksomheder blev dygtigere, faldt prisen p&#xE5; begge dele, og flere kunne f&#xE5; r&#xE5;d til elektronikken og <em>papkortene</em> der nu for l&#xE6;ngst var blevet effektiviseret til fleksible folier belagt med magnetiseret materiale - b&#xE5;nd, skiver, osv.</p><p>Tilg&#xE6;ngeligheden satte flere og flere i stand til at l&#xE6;re (sig selv) at programmere og fordi virksomhederne kunne se &#xF8;konomiske fordele ved at standardisere dele af udstyret, blev det muligt at programmere til flere leverand&#xF8;rers udstyr. Det skabte virksomheder i en helt ny branche: software udvikling.</p><p>I et helt andet spor og med et helt andet form&#xE5;l, f&#xF8;dtes en &quot;ting&quot; som sammen med computeren og programmets f&#xF8;dsel, skulle &#xE6;ndre verden aldeles: <a href="https://dictionary.cambridge.org/dictionary/english/internet?ref=cunnin.gs#google_vignette" rel="noreferrer">Internettet</a>.</p><p>Det var igen universiteterne som var spydspidsen, og som i &apos;60-erne havde overtalt det amerikanske forsvar til at kaste penge efter udviklingen af en sikker og ikke mindst fejltolerant m&#xE5;de at kommunikere p&#xE5; (mellem f.eks. atommissil siloerne og Forsvarskommandoen). Forskerne n&#xF8;d snart godt af at kunne dele viden mange gange hurtigere end tidligere (hvor viden f&#xF8;rst skulle trykkes og derefter omdeles i i b&#xF8;ger og rapporter). Det var der ogs&#xE5; virksomheder, som kunne se perspektiver i, og som blev dygtige til b&#xE5;de at bygge udstyret, og programmere det effektivt. Hvor effektivt Internettet er som distributionskanal for alt digitalt indhold er de fleste klar over i dag.</p><p>Med computere, programmer, og distributionskanaler, har vi nu n&#xE6;sten alle byggestenene p&#xE5; plads &#x2013; men hvorfor &#xE6;ndre noget, der virker som en s&#xE6;rdeles effektiv (og indbringende) cocktail? Idealisme m&#xE5;ske?! Hvad var motivationen?</p><p>Programm&#xF8;rer er/var en slags &quot;h&#xE5;ndv&#xE6;rkere&quot; og n&#xE5;r de ikke kan f&#xE5; et program til at virke, som de gerne vil, skriver de det om. Det var let i starten, fordi de nemlig skrev alt selv. Med tiden blev det nemmere/billigere at k&#xF8;be dele af programmet af andre, og selv kun &#xE6;ndre noget af programmet. Men n&#xE5;r den del, man havde k&#xF8;bt, s&#xE5; pludselig indeholdt en fejl, kunne man vente fuldst&#xE6;ndig paralyseret indtil leverand&#xF8;ren havde rettet fejlen. &#xD8;nskede man at delen skulle indeholde en funktion, som ikke fandtes, og leverand&#xF8;ren ikke havde lyst til at bygge den funktion ind i delen &#x2013; s&#xE5; var man paralyseret igen!</p><h2 id="reaktion">Reaktion</h2><p>Hos nogen programm&#xF8;rer aff&#xF8;dte det den holdning at alle skulle have ret til at skille deres programmer ad, forst&#xE5; hvordan de virkede, og &#xE6;ndre dem, hvis de mente det var n&#xF8;dvendigt.</p><p>Andre programm&#xF8;rer var mere optaget af at f&#xE5; programmer til at virke p&#xE5; forskelligt udstyr, minimere sikkerhedsrisici ved at bruge programmerne og &#xF8;ge deres p&#xE5;lidelighed og effektivitet.</p><p>Jeg bliver n&#xF8;dt til at fremh&#xE6;ve &#xE9;n person, som i s&#xE6;rdeleshed har v&#xE6;ret instrumentel for dannelsen af begrebet open source. Hans navn er Linus Torvalds og han er oprindeligt finne. Hans evne til at bygge meget effektive programmer er bevaret for altid i 2 &quot;produkter&quot; som er hans v&#xE6;rk: kernen til operativsystemet Linux, og software-versionsstyring v&#xE6;rkt&#xF8;jet Git. Han er h&#xE5;ndv&#xE6;rkeren, der kan se noget ineffektivt, og bygger noget, der er bedre. I tilf&#xE6;ldet Linux fandtes allerede operativsystemer, som Windows og UNIX, men sammen med mere end 16000 frivillige hj&#xE6;lpere, er det lykkedes at g&#xF8;re Linux s&#xE5; popul&#xE6;rt at selv IBM begyndte at pege p&#xE5; det, som svaret p&#xE5;, hvordan deres udstyr kunne styres &#x2013; og, vel at m&#xE6;rke, uden at Linus Torvalds k&#xF8;rte en eneste avisreklame, TVspot eller Facebook kampagne (Mark Zuckerberg var f&#xF8;rst lige begyndt i skole, da Linus i 1991 frigav de f&#xF8;rste linjer af koden til Linux). </p><p>Grunden til at n&#xE6;vne Linus frem for en <a href="https://www.google.com/search?q=important+people+behind+the+advent+of+open+source&amp;rlz=1C5CHFA_enDK952DK952&amp;oq=important+people+behind+the+advent+of+open+source&amp;gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTExMjI2ajBqN6gCALACAA&amp;sourceid=chrome&amp;ie=UTF-8&amp;ref=cunnin.gs" rel="noreferrer">lang, lang r&#xE6;kke af andre betydningsfulde</a>, er den, at han med sin frigivelse af koden til operativsystemet, p&#xE5; sin vis skabte pr&#xE6;cedens for at stille adgangen til koden frit tilg&#xE6;ngelig &#x2013; ikke at han n&#xF8;dvendigvis var den f&#xF8;rste, men den kode, han delte, fik kolossal betydning for en hel branche siden da; og med <a href="https://github.com/?ref=cunnin.gs" rel="noreferrer">Git</a> gjorde han det 14 &#xE5;r senere muligt, for mange udviklere (programm&#xF8;rer) at arbejde sammen om at bygge p&#xE5; det samme program, samtidigt, uden (eksempelvis) at skulle koordinere deres indsatser &quot;a priori&quot;.</p><p>N&#xE5;r organisationer og virksomheder i dag i stigende grad begynder at interessere sig for, om der mon skulle v&#xE6;re et <em>open source alternativ</em> til netop deres behov, s&#xE5; skyldes det, med titlen p&#xE5; Lemony Snickets bogserie, <em>a series of unfortunate events</em>. </p><ol><li>De fleste kan se et monopol, n&#xE5;r det findes. Det er der ikke i IT branchen, men den er <strong>foruroligende t&#xE6;t p&#xE5; et oligopol</strong>, best&#xE5;ende af meget f&#xE5;, meget store amerikanske tech-virksomheder, med Microsoft, Google, og Amazon som de helt centrale akt&#xF8;rer &#x2013; det er skidt for konkurrencen og is&#xE6;r: prisdannelsen</li><li>Den <strong>geopolitiske scene</strong> viser tegn p&#xE5; at et magtskifte er undervejs, og derfor sikrer is&#xE6;r virksomheder sig ved, med Hr M&#xF8;llers ord: <em>at udvise rettidig omhu og undg&#xE5; at et tab rammer dem</em></li><li>Risikoen for <strong>databrist</strong> har v&#xE6;ret tilstede i en &#xE5;rr&#xE6;kke/altid. EU fors&#xF8;gte i midten af 10&apos;erne at s&#xE6;tte det i fokus med GDPR (det blev uheldigvis en papirtiger som fodrede advokater, revisorer og konsulentbranchen). Med <strong>den siddende administration i USA</strong> er risikoen mangedoblet</li><li><strong>Manglende skal&#xE9;rbarhed </strong>i omkostningerne. Det har aldrig v&#xE6;ret en hemmelig, at der er en meromkostning forbundet med at uddelegere ansvar og kompetence, men det er sandsynligvis kommet bag p&#xE5; en del, hvor stor meromkostningen er &#x2013; og at den ikke skalerer godt</li><li><strong>Kompleksiteten</strong> i forretningssystemerne (de mange programmer, som medarbejdere benytter i b&#xE5;de produktion og administration) har n&#xE5;et et omfang, der truer med at <strong>&#xF8;del&#xE6;gge virksomheders stordriftsfordele</strong>, og begrave dem i hvad der til alle tider har v&#xE6;ret enhver organisations og forretningsid&#xE9;s banemand: <strong>bureaukrati</strong>.</li><li><strong>Professionaliserede k&#xF8;bere</strong>. Private virksomheders (men ogs&#xE5; succesfulde offentlige institutioners) konkurrencem&#xE6;ssige fordele ligger i deres unikke h&#xE5;ndtering af kunden, produktet, distributionen, og/eller transaktionen, og det DNA afspejles altid i forretningssystemerne. IT branchen har solgt &quot;standard systemer&quot; siden jeg var l&#xE6;rling, men leveret (og levet af) tilpasninger til de samme, og det &quot;lock-in&quot; (hvor man l&#xE5;ses til en leverand&#xF8;r og deres &quot;tilpasninger&quot;) er k&#xF8;bere i dag i stigende grad blevet s&#xE5; erfarne i at gennemskue, at et alternativ bliver relevant</li></ol><h2 id="er-open-source-l%C3%B8sningen">Er open source l&#xF8;sningen?</h2><p>L&#xF8;ser &#xE5;ben kildekode de problemer? Ja, hvis eftersp&#xF8;rgslen efter fx Microsoft&apos;s produkter udebliver, reduceres deres markedsrolle. Spredes eftersp&#xF8;rgslen p&#xE5; et utal af konkurrerende produkter (eksisterende s&#xE5;vel som nye der m&#xE5;tte opst&#xE5; som f&#xF8;lge af muligheden), vil det &#xF8;ge konkurrencen, fremme prisdannelsen og innovationen.</p><p>V&#xE6;rkt&#xF8;jers kompleksitet har en afg&#xF8;rende indflydelse p&#xE5; deres ibrugtagning, accept blandt brugere, og udnyttelsesgraden af dem. Adgangen til at tilpasse eller direkte fjerne funktionalitet vil uomtvisteligt reducere kompleksitet, og skabe muligheden for at fjerne eller mindste risikoen for databrist (n&#xE5;r v&#xE6;rkt&#xF8;jets virke kan analyseres, revideres, og reproduceres).</p><p>Skal&#xE9;rbarheden i anvendelsen (og givetvis ogs&#xE5; omkostningerne) vil &#xF8;ges drastisk i takt med reduktionen i kompleksitet. IT branchen har vist det i mindst 15-20 &#xE5;r! SaaS (hvor du betaler hver m&#xE5;ned for at bruge et v&#xE6;rkt&#xF8;j) var svaret p&#xE5; en r&#xE6;kke sp&#xF8;rgsm&#xE5;l: hvordan kan vi maksimere indtjeningen p&#xE5; den kode vi skriver? hvordan kan vi reducere dybden af &apos;salgstrakten&apos; = g&#xF8;re det nemmere at s&#xE6;lge kunden p&#xE5; l&#xF8;sningen? hvordan kan vi sikre vores customer retention (hvordan holder vi kunderne fast)? Fjern kompleksitet (g&#xF8;r det nemmere for kunden at &quot;forst&#xE5;&quot; produktet) ved at begr&#xE6;nse produktets funktionelle dom&#xE6;ne.</p><p>Der venter en ny verdensorden, nye v&#xE6;rdik&#xE6;der, m&#xE5;ske en ny &quot;basis valuta&quot; til erstatning for dollaren. Der venter en n&#xE6;sten overv&#xE6;ldende strukturel &#xE6;ndring af samfundet, hvor teknologi kommer til at spille en endnu st&#xF8;rre rolle. Vi kender nogle f&#xE5; af begreberne allerede: robotter, AI, mfl. Vi ved ogs&#xE5; at <em>big is beautiful </em>men at<em> complexity kills</em> og m&#xE5;ske lever softwarel&#xF8;sninger ikke l&#xE6;nge nok til at de kan retf&#xE6;rdigg&#xF8;re fler-faktor omkostninger i tilpasninger &#x2013; og hvis det s&#xE5; er standard, som alle bruger, vil det s&#xE5; ikke v&#xE6;re n&#xE6;rliggende at forlange, at dette &quot;fundament&quot; er kendt, revid&#xE9;rbart, reproduc&#xE9;rbart; at det er standarden, at det er open source?</p><p>I d&#xE9;n kalejdoskopiske kontekst er open source m&#xE5;ske det bedste svar, i det mindste  p&#xE5; den kortere bane.</p>]]></content:encoded></item><item><title><![CDATA[Hotwiring taggables]]></title><description><![CDATA[<p>We found ourselves wanting a tagging engine for <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">M O R T I M E R</a> &#x2013; <em>your fastest way from job done to invoice sent </em>BTW; and when evaluating the existing gems my hands trembled just slightly: the &apos;best in class&apos; with 20+ mio downloads and shy</p>]]></description><link>https://cunnin.gs/categorizing-tags/</link><guid isPermaLink="false">6824343f029b6e0007775331</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Mon, 19 May 2025 13:59:31 GMT</pubDate><media:content url="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-19-kl.-15.58.03.png" medium="image"/><content:encoded><![CDATA[<img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-19-kl.-15.58.03.png" alt="Hotwiring taggables"><p>We found ourselves wanting a tagging engine for <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">M O R T I M E R</a> &#x2013; <em>your fastest way from job done to invoice sent </em>BTW; and when evaluating the existing gems my hands trembled just slightly: the &apos;best in class&apos; with 20+ mio downloads and shy of 200 contributors and yet well over 250 issues &#x2013; nah, don&apos;t think so...</p><p>So then, surely someone else did this years ago, right? Well - Jos&#xE9; Farias shared <a href="https://github.com/josefarias/hotwire_combobox?ref=cunnin.gs" rel="noreferrer">hotwire_combobox</a> about 2 years ago, that&apos;s how close I&apos;ve been able to get. A programmer, Yair Even Or, did start sharing <a href="https://github.com/yairEO/tagify?ref=cunnin.gs" rel="noreferrer">Tagify</a> 9 years ago, and that project indeed has a lot of potential, but again - 70 issues - and it&apos;s a <em>js-soup</em>, but then again, it&apos;s been a valuable smorg&#xE5;sbord of implementation details! We will not even reach it to it&apos;s socks &#x2013; for a long, long time, if ever...</p><p>Ohh - and in case you wonder - I&apos;ve got a Copilot eagerly chiming along the way which will have us go back and do &apos;re-runs&apos; on a number of occasions but that&apos;s 2025 for you right there I guess &#x1F61C;</p><h3 id="use-cases">Use cases</h3><p>I better get the use cases straight, first! This sketch done in one of my top favorite tools, <a href="https://whimsical.com/?ref=cunnin.gs" rel="noreferrer">Whimsical</a> (yes I&apos;m a customer, and no, I do not get credits for mentions) should give you a sense of the domain:</p><figure class="kg-card kg-image-card"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-15-kl.-11.04.39.png" class="kg-image" alt="Hotwiring taggables" loading="lazy" width="864" height="432" srcset="https://cunnin.gs/content/images/size/w600/2025/05/Sk-rmbillede-2025-05-15-kl.-11.04.39.png 600w, https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-15-kl.-11.04.39.png 864w" sizes="(min-width: 720px) 720px"></figure><p>Whether the input comes verbatim through some AI speech-to-text or will be even further <em>digested</em> by other LLM&apos;s spitting out stuff like</p><pre><code class="language-json">{
  time_material: {
    time: &quot;1.25&quot;,
    task: &quot;QA&quot;,
    location: &quot;Lab1&quot;,
    user: &quot;bio intern&quot;,
    started_at: &quot;6:45PM&quot;
  }
}</code></pre><p>really is non-essentiel.</p><h3 id="uiframing-use-cases">UI - framing use cases</h3><p>With the title encompassing <em>hotwire</em> I bet you&apos;re on the edge of your stool waiting for the implementation details, but we need to set the expectations - so here is the &quot;MVP&quot; so to speak:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-17-kl.-07.18.24.png" class="kg-image" alt="Hotwiring taggables" loading="lazy" width="196" height="82"><figcaption><span style="white-space: pre-wrap;">initial frame</span></figcaption></figure><p>Looking like an ordinary input - but it has a trick or two up it&apos;s sleeves &#x1F609;</p><p>Like when you start typing the input will assist you with suggestions matching what you have typed so far</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-17-kl.-07.24.11.png" class="kg-image" alt="Hotwiring taggables" loading="lazy" width="251" height="199"><figcaption><span style="white-space: pre-wrap;">lookup frame</span></figcaption></figure><p>To mobile users what follows is of no interest. </p><p>From here you can go different ways. Input a comma or period to tell the input field that your tag is done and should be added as is. Use the arrow keys Up and Down to navigate the suggestions. Use Enter to select a particular suggestion. Use Escape to remove the dropdown</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-17-kl.-07.34.00.png" class="kg-image" alt="Hotwiring taggables" loading="lazy" width="223" height="124"><figcaption><span style="white-space: pre-wrap;">selected frame</span></figcaption></figure><p>When selected you can use Backspace to go back and start editing the tag or start typing the next tag.</p><p>There is one more frame to consider. With very long tags (multi-word tags like <strong>non-stick polycarbonate based appliances</strong>) or a whole <em>cloud of tags</em> you will find yourself running out of chrome so to speak &#x2013; so the input field should (automagically preferably) consider where to place the added tags, inline or below</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-17-kl.-07.39.47.png" class="kg-image" alt="Hotwiring taggables" loading="lazy" width="223" height="133"><figcaption><span style="white-space: pre-wrap;">selected large frame</span></figcaption></figure><h3 id="uiimplementation">UI - implementation</h3><p>Finally &#x2013; that was quite a, ehhh <em>foreplay</em>, don&apos;t you think? Let&apos;s get dirty, shall we?</p><h4 id="initial-frame">Initial Frame</h4><p>We use <a href="https://phlex.fun/?ref=cunnin.gs" rel="noreferrer">Phlex</a> extensively in  <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">M O R T I M E R</a>  and because Brad Gessler did such a tremendous job on <a href="https://github.com/rubymonolith/superform?ref=cunnin.gs" rel="noreferrer">Superform</a>, our &apos;forms&apos; &#x2013; at least the CRUD ones, mostly looks like this:</p><pre><code class="language-ruby">class Projects::Form &lt; ApplicationForm
  def view_template(&amp;)
    row field(:name).input().focus
    row field(:description).input()
    row field(:start_date).datetime(class: &quot;mort-form-datetime&quot;)
    row field(:end_date).datetime(class: &quot;mort-form-datetime&quot;)
    row field(:state)
      .select(Project.project_states, class: &quot;mort-form-select&quot;)
    row field(:budget).input()
    row field(:is_billable).boolean(class: &quot;mort-form-bool&quot;)
    row field(:is_separate_invoice).boolean(class: &quot;mort-form-bool&quot;)
    ...8&lt;..
    row field(:actual_minutes).input()
  end
end
</code></pre><p>If you&apos;d like to read more about how  M O R T I M E R  utilises Phlex leave a comment. Anyways, we&apos;re not ready to build the &apos;shrink-wrapped&apos; version just yet so we&apos;ll start with the first &quot;frame&quot; and write the necessary test &#x2013; by the way: Phlex is sooo easy to test being <em>just</em> PORO (plain old ruby objects), but first let&apos;s make sure the basic input field is in place</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">require &quot;application_system_test_case&quot;

class TagTest &lt; ApplicationSystemTestCase
  # setup do
  #   @tenant = tenants(:one)
  #   @user = users(:superadmin)
  # end

  test &quot;the tag to have an input field&quot; do
    output = render TagComponent.new
    assert_match &quot;&lt;input&quot;, output.to_s
  end
end
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">test/components/tag_test.rb</span></p></figcaption></figure><p>That obviously does turn out red &#x2013; but is easily fixed with</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">class TagComponent &lt; ApplicationComponent
  def view_template
    input(type: &quot;text&quot;, value: &quot;tag1, tag2&quot;)
  end
end
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">views/components/tag_component.rb</span></p></figcaption></figure><p>Ok - we&apos;re moving, GREAT! The input element is BTW <em>the</em> element holding on to what every values are provided up front as well as added during an editor session (a lot more on this later).</p><p>Time for som Hotwiring - don&apos;t you think? Here we go: The <code>&lt;turbo-frame&gt;&lt;/turbo-frame&gt;</code> will allow Rails and Turbo/Stimulus to update the DOM &apos;in flight&apos;:</p><pre><code class="language-ruby">  test &quot;the tag to have a turbo_frame &quot; do
    output = render TagComponent.new 
      resource: TimeMaterial.new, 
      field: :task_comment
      
    assert_match &quot;&lt;turbo-frame id=\&quot;tag&quot;, output.to_s
  end</code></pre><p>The component arguments is just us slowly preparing to move this into actually serving  proper values for ActiveRecord objects. Making this test green requires a few changes, the important ones being</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">  include Phlex::Rails::Helpers::TurboFrameTag

  def view_template
    turbo_frame_tag &quot;#{Current.get_user.id}_tag&quot; do
      div(data: { controller: &quot;tag&quot; }) do
        @editable ? editable_view : show_view
      end
    end
  end</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">views/components/tag_component.rb</span></p></figcaption></figure><p>This is a naiive implementation (think: multiple forms in the view, multiple fields in a form, more contexts, and what not) but will restrict the turbo_stream to whatever the current_user is meddling with, at least.</p><p>We will skip adding any label to the field, and styling is also of lesser interest right now. </p><h4 id="lookup-frame">Lookup Frame</h4><p>Building a list of autocomplete elements is what we will focus on. We will need a test for that (and add 2-3 tags to the <code>fixtures/tags.yml</code></p><pre><code class="language-ruby">  test &quot;the tag to have a lookup container&quot; do
    @tags = Tag.all
    m = &quot;&lt;div id=\&quot;time_material-task_comment-lookup-container\&quot;&quot;
    output = render TagComponent.new 
      resource: TimeMaterial.new, 
      field: :task_comment, 
      resources: Tag.all,
      search: &quot;do&quot;
      
    assert_match m, output.to_s
    assert_match tags(:one).name, output.to_s
  end
</code></pre><p>We also make sure the tags are listed with the last assert_match. In order for this to turn green we will change the component to look like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">class TagComponent &lt; ApplicationComponent

  def initialize(resource:, field:, resources: nil, search: &quot;&quot;)
    @resource = resource
    @field = field
    @resources = resources
    @search = search
  end

  def view_template
    turbo_frame_tag &quot;#{Current.get_user.id}_tag&quot; do
      div(data: { controller: &quot;tag&quot; }) do
        input(type: &quot;text&quot;,
          data: { tag_target: &quot;input&quot;, action: &quot;keyup-&gt;tag#keyup&quot; },
          name: resource_field(&quot;%s[%s]&quot;),
          id: resource_field,
          class: @field_class,
          value: @search)
    
        render_tags_list      
      end
    end
  end

  def render_tags_list
    return if @resources.nil?
    
    div(id: resource_field(&quot;%s-%s-lookup-container&quot;) do
      @resources.each do |tag|
        a(href: &quot;#&quot;, data: { action: &quot;click-&gt;tag#pick&quot; }) { tag.name }
      end
    end
  end

  def resource_field(joined = &quot;%s_%s&quot;)
    joined % [ @resource.class.to_s.underscore, @field.to_s.underscore ]
  end
  
end
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">views/components/tag_component.rb</span></p></figcaption></figure><p>We like the &#x1F7E2; so much! Now for this &apos;frame&apos; to ever come to fruition, we&apos;ll have to add some <em>stimuli</em> or rather just one and we&apos;ll use this command to do it: <code>rails g stimulus tag</code> that we then edit and make look like this</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import { Controller } from &quot;@hotwired/stimulus&quot;
import { get } from &quot;@rails/request.js&quot;;

// Connects to data-controller=&quot;tag&quot;
export default class extends Controller {
  static targets = [
    &quot;input&quot;,
    &quot;output&quot;,
    &quot;selectedTags&quot;,
    &quot;tagList&quot;]

  lastSearch = null;

  connect() {
    setTimeout(() =&gt; {
      if (this.inputTarget.textContent == undefined)
        this.inputTarget.textContent = &quot;&quot;;
      this.lastSearch = this.inputTarget.textContent;
    },10)
    this.inputTarget.focus();
  }

  // put the cursor at the end of the &apos;input&apos;
  focus(event) {
    event.preventDefault();
    let caret = document.createRange();
    caret.selectNodeContents(this.inputTarget);
    caret.collapse(false);
    const sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(caret);
  }

  keyup(event){
    this.lookup(event);
  }

  keydown(event) {
  }

  lookup(event) {
    if (event.target.value.length &gt; 1) {
      get(`/tags/tags?search=${event.target.value}`, {
        responseKind: &quot;turbo-stream&quot;
      })
    }
  }
}
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">javascript/controllers/tags_controller.js</span></p></figcaption></figure><p>We are not nearly there - and we are not doing any system tests yet, but that calls for a fresh cup of coffee I think! We&apos;re not home yet but gettin&apos; there, and while you get that &#x2615; here is the controller handling the &apos;get&apos; action</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">class TagsController &lt; MortimerController
  def tags
    tags = Tag.by_tenant
      .where(&quot;name LIKE ?&quot;, &quot;%#{params[:search]}%&quot;)
      .order(&quot;name ASC&quot;)
      .limit(10)
    respond_to do |format|
      format.turbo_stream { 
        render partial: &quot;tags/tags&quot;, locals: { tags: tags } 
      }
    end
  end
end
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">controllers/tags_controller.rb</span></p></figcaption></figure><p>it&apos;s not done - don&apos;t worry, but more like a WIP. I know &#x2013; it&apos;s not RESTful as such but one extra route on the tags resource for this specialized purpose seems like a small price to pay.</p><p>The accompanying partial with the &quot;tags/tags&quot; reference above:</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">&lt;%= turbo_stream.replace(&quot;#{Current.get_user.id}_tag&quot;) do %&gt;
&lt;%=   render TagComponent.new(resource: Tag.new, 
        field: :name, 
        resources: tags, 
        search: params[:search], 
        show_label: true, 
        field_class: &quot;mort-form-text&quot;, 
        label_class: &quot;text-red-500&quot;, 
        value_class: &quot;mr-5&quot;, 
        editable: true) %&gt;
&lt;% end %&gt;</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">views/tags/_</span><i><em class="italic" style="white-space: pre-wrap;">tags.turbo</em></i><span style="white-space: pre-wrap;">stream.erb</span></p></figcaption></figure><p>That, mostly, finished the lookup frame in our first run (yes I reckon there will be at least a second run). Now on to the</p><h4 id="selected-frame">Selected Frame</h4><p>We&apos;ll press on with a test</p><pre><code class="language-ruby">test &quot;the tag to have a selected container with a selected tag&quot; do
    @tags = Tag.all
    m = &quot;&lt;div id=\&quot;time_material-task_comment-selected-container\&quot;&quot;
    output = render TagComponent.new 
      resource: TimeMaterial.new, 
      field: :task_comment, 
      resources: @tags, 
      value: [ tags(:one).name ]
      
    assert_match m, output.to_s
    assert_match tags(:one).name, output.to_s
  end</code></pre><p>Hmmm &#x1F534; &#x2013; not totally unexpected but; &#x1F62E; Second run so soon?! Who&apos;d guess &#x1F60E;</p><p>Here&apos;s the predicament we&apos;re in: As per our use case #1 (initial frame) we&apos;re good, but now we need to paint a box <strong>inside</strong> the input box! That&apos;s totally not going to fly. What to do?</p><p>First we have to go back and refactor the initial frame - yep, second run! While we&apos;re at it let&apos;s clean up the methods</p><pre><code class="language-ruby">class TagComponent &lt; ApplicationComponent

  def initialize(resource:, field:, resources: nil, search: &quot;&quot;, value: &quot;&quot;)
    @resource = resource
    @field = field
    @resources = resources
    @search = search
    @value = value.class == String ?
      value :
      value.join(&quot;, &quot;)
  end

  def view_template
    turbo_frame_tag &quot;#{Current.get_user.id}_tag&quot; do
      div(class: &quot;mort-field&quot;, data: { controller: &quot;tag&quot; }) do
        #
        # making the input a hidden one!
        input(type: &quot;hidden&quot;,
          name: resource_field(&quot;%s[%s]&quot;),
          id: resource_field,
          value: @value,
          data: { tag_target: &quot;output&quot; })
        @editable ? editable_view : show_view
      end
    end
  end

  def editable_view
    div(class: &quot;flex flex-col&quot;, data: { action: &quot;click-&gt;tag#focus&quot; }) do
      label_container
      div(class: &quot;inline-flex w-full \
        rounded-md border-0 bg-white py-1.5 pl-1 \
        text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 &quot;) do
        selected_container
      end
      div(class: &quot;relativ&quot;) do
        render_tags_list
      end
    end
  end

  
  def selected_container
    div(id: resource_field(&quot;%s-%s-selected-container&quot;), 
      data: { tag_target: &quot;selectedTags&quot; }, class: &quot;flex flex-wrap&quot;) do
      @value.each do |tag|
        span(class: &quot;flex items-center&quot;) do
          a(href: &quot;#&quot;, 
            data: { action: &quot;click-&gt;tag#removeTag&quot;, 
            id: tag.id }, class: &quot;ml-0.5 mb-0.5&quot;) do
            span(class: &quot;flex items-center bg-gray-200 \
              text-gray-700 px-2 py-1 rounded-md text-sm&quot;) do
              span { tag.name }
              render Icons::Cancel.new(css: &quot;ml-2 h-4 w-4 text-gray-400&quot;)
            end
          end
        end
      end
      editor_field
    end
  end


  def editor_field
    span(contenteditable: true,
      data: {
        tag_target: &quot;input&quot;,
        action: &quot;keydown-&gt;tag#keydown keyup-&gt;tag#keyup focus-&gt;tag#focus&quot;,
        placeholder: I18n.t(&quot;components.tag.#{@field}&quot;)
      },
      # hmm CSS does not seem to bite!?
      style: &quot;border: none; outline: none;&quot;,
      class: &quot;grow ml-0.5 px-1&quot;,
      name: resource_field(&quot;%s[%s]-input&quot;),
      id: resource_field(&quot;%s_%s-input&quot;)) { @search.html_safe }
  end
  
end</code></pre><p>(The backslash on the attributes means you should keep the strings together &#x2013; only there for readability here!)</p><p>Much better - now we just need to sprinkle CSS all over the place (under the influence of Tagify, remember?)</p><figure class="kg-card kg-code-card"><pre><code class="language-css">.mort-form-tag {
  @apply inline-flex w-full rounded-md border-0 bg-white py-1.5 pl-3 text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300;
}

[contenteditable=&quot;true&quot;]:active,
[contenteditable=&quot;true&quot;]:focus {
  border: none;
  outline: none;
}

.tag-input {
  flex-grow: 1;
  display: inline-block;
  position: relative;
  margin: 5px;
  padding: var(--tag-pad);
  line-height: normal;
  white-space: pre-wrap;
  box-sizing: inherit;
  overflow: hidden;

  &amp;::before {
    content: attr(data-placeholder);
    width: 100%;
    height: 100%;
    margin: auto 0;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    pointer-events: none;
    position: absolute;
    opacity: 0;
    transition: all 0.5s;
  }
  &amp;::after {
    content: attr(data-suggest);
    display: inline-block;
    vertical-align: middle;
    position: absolute;
    min-width: calc(100% - 1.5em);
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: pre;
    opacity: .3;
    pointer-events: none;
    max-width: 100px;
    transition: all 0.5s;
  }
}
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">assets/tailwind/application.css</span></p></figcaption></figure><p>The <code>.mort-form-tag</code> and 5 topmost lines of CSS are of my own &apos;googling&apos;, though. Turns out (at least some) browsers by default decorate a contenteditable element.</p><p>The <code>tag_controller.js</code> even had to have a few &quot;coats of paint&quot; :</p><pre><code class="language-javascript">
  lookup(txt) {
    if (txt.length &gt; 1 &amp;&amp; txt !== this.lastSearch) {
      this.lastSearch = txt;
      get(`/tags/tags?search=${txt}`, {
        responseKind: &quot;turbo-stream&quot;
      });
    }
  }

  addTag(event) {
    event.preventDefault();
    let tags = this.outputTarget.value;
    let data = this.inputTarget.textContent;
    let url = encodeURI(`/tags/tags?add_tag=${data}&amp;value=${tags}`);
    get(url, {
      responseKind: &quot;turbo-stream&quot;,
    });
  }

  removeTag(event) {
    event.preventDefault();
    const tag = event.currentTarget;
    const selectedTags = this.selectedTagsTarget;
    if (selectedTags) {
      const tagItem = selectedTags
        .querySelector(`[data-id=&quot;${tag.dataset.id}&quot;]`);
      if (tagItem) {
        let tags = this.outputTarget.value.split(&quot;,&quot;);
        tags = tags.filter(t =&gt; t !== tag.dataset.id).join(&quot;,&quot;);

        let url = encodeURI(
          `/tags/tags?` +
          `search=${this.inputTarget.textContent}&amp;` +
          `value=${tags}`
        );
        
        get(url, {
          responseKind: &quot;turbo-stream&quot;,
        });
      }
    }
  }


  pick(event) {
    event.preventDefault();
    if (this.outputTarget.value === undefined) {
      this.outputTarget.value = &quot;&quot;;
    }
    if (this.outputTarget.value != &quot;&quot;) {
      this.outputTarget.value = this.outputTarget.value + &quot;,&quot;;
    }
    let url = encodeURI(
      `/tags/tags?`+
      `value=${this.outputTarget.value}${event.target.textContent}`
    );
    
    get(url, {
      responseKind: &quot;turbo-stream&quot;,
    });
    this.tagListTarget.classList.add(&apos;hidden&apos;);
  }
</code></pre><p>Now we are getting the hang of it &#x2013; so let&apos;s refactor somewhat, again.</p><p>First, we need to realize that keeping a tab on the tags is a challenge with words - so we will hold their ID&apos;s instead. <code>value = [ ID, ID, .., ID ]</code></p><p>Let&apos;s have a test for that &#x2013; in fact all we need to do is change the last test ever so much:</p><pre><code class="language-ruby">
  test &quot;the tag to have a selected container with a selected tag&quot; do
    tags = []
    m = &quot;&lt;div id=\&quot;time_material-task_comment-selected-container\&quot;&quot;
    output = render TagComponent.new 
      resource: TimeMaterial.new, 
      field: :task_comment, 
      resources: tags, 
      value: [ tags(:one).id ]
      
    assert_match m, output.to_s
    assert_match &quot;data-id=\&quot;#{tags(:one).id}\&quot;&quot;, output.to_s
    assert_match tags(:one).name, output.to_s
  end</code></pre><p>Now, here&apos;s one for the &quot;buffs&quot; reading this: the second <code>assert_match</code> probably does not sit well with you! &quot;Don&apos;t test the implementation - test the consequence/result&quot; I hear you yell, and you&apos;re right, alright?! (and I&apos;m painfully aware that with this entire post I probably commit enough &apos;design/developer sins&apos; to only ever be ridiculed and made the laughing stock &#x2013; but such is life) &#x1F937;&#x200D;&#x2642;&#xFE0F;</p><p>The TagsController was long overdue for some serious refactor</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">  def tags
    if params[:add_tag].present?
      tag = Tag.find_or_create_by(name: params[:add_tag], 
        tenant_id: Current.get_tenant.id, 
        created_by: Current.get_user)
    end
    unless params[:search].blank?
      tags = Tag.by_tenant
        .where(&quot;name LIKE ?&quot;, &quot;%#{params[:search]}%&quot;).order(&quot;name ASC&quot;)
        .limit(10)
      search = params[:search]
    else
      tags = []
      search = &quot;&quot;
    end
    if params[:value].blank?
      value = tag.present? ? [ tag ] : []
    else
      ids = params[:value].to_s.split(&quot;,&quot;).map(&amp;:strip)
      ids.push tag.id if tag.present?
      value = Tag.by_tenant.where(id: ids) rescue []
    end

    respond_to do |format|
      format.turbo_stream { 
        render partial: &quot;tags/tags&quot;, locals: { tags:, search:, value: } 
      }
    end
  end
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">controllers/tags_controller.rb</span></p></figcaption></figure><p>Four issues remain (+ the ones you might spot):</p><ol><li>doing the persistence of tags on any particular resource&#x200A;&#x2014;&#x200A;like a TimeMaterial ActiveRecord</li><li>Test how editing existing ActiveRecord objects fare</li><li>Solve the layout automagically when tags get crowded in the input</li><li>Adding more than one tag input field on a form</li></ol><p>Leave a note - or perhaps your take on this. You are always welcome to visit with us at <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">M O R T I M E R</a>.</p><p>&#x1F64F;</p>]]></content:encoded></item><item><title><![CDATA[The Generic Modal]]></title><description><![CDATA[<p>What is a modal even? How could it be generic? Questions seems to pile easily. I&apos;ll do my best to answer them as we build exactly that - a generic modal.</p><p>I&apos;m using the SaaS product we&apos;re building &#x2013; it&apos;s called <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">M</a></p>]]></description><link>https://cunnin.gs/the-generic-modal/</link><guid isPermaLink="false">6822f0a7029b6e000777517a</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Tue, 13 May 2025 15:44:21 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1496045737814-5ef82eb050fd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxnZW5lcmljJTIwd2luZG93fGVufDB8fHx8MTc0NzE1MTEwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1496045737814-5ef82eb050fd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxnZW5lcmljJTIwd2luZG93fGVufDB8fHx8MTc0NzE1MTEwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="The Generic Modal"><p>What is a modal even? How could it be generic? Questions seems to pile easily. I&apos;ll do my best to answer them as we build exactly that - a generic modal.</p><p>I&apos;m using the SaaS product we&apos;re building &#x2013; it&apos;s called <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">M O R T I M E R</a> and it affords you the fastest from job done to invoice sent.</p><p>It&apos;s a Rails 8 application but you could strap this on a Rails 7 application as well, I believe.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-14-kl.-08.05.04-1.png" class="kg-image" alt="The Generic Modal" loading="lazy" width="961" height="497" srcset="https://cunnin.gs/content/images/size/w600/2025/05/Sk-rmbillede-2025-05-14-kl.-08.05.04-1.png 600w, https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-14-kl.-08.05.04-1.png 961w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">How the generic modal presents itself &#x2014; when tasked with deleting something</span></figcaption></figure><h2 id="a-modal">A modal</h2><p>Modals are a <a href="https://en.wikipedia.org/wiki/Modal_window?ref=cunnin.gs" rel="noreferrer">graphical control element subordinate to an application&apos;s main window</a> according to Wikipedia. Usually it&apos;s implemented with a backdrop indicating that focus has now shifted to the modal element only.</p><p>Javascript fellows <code>prompt</code> and <code>alert</code> displays modals of sorts. We can go quite further than that but we will start there.</p><p>Modals will have 3 variables defining their capabilities:</p><h4 id="flavor">Flavor</h4><p>We will say that modals have a flavor. From very basic to very advanced flavors will tell us what the modal will be capable of. <code>alert</code> is one flavor that only is capable of posting a &apos;warning sign&apos;. <code>prompt</code> is just a bit more advanced affording a accept or refuse reply - think deleting posts etc. <code>guide</code> is &#x2013; at the moment &#x2013; the most advanced modal I can think of. It provides information, allows for inputting information, and may advance forward or go backwards in a preset number of views/steps.</p><h4 id="modalform">Modal_form</h4><p>Modal form partials are labeled by their name in the views/modal/ folder - like fx &apos;_delete.html.erb&apos;</p><h4 id="modalnextstep">Modal_next_step</h4><p>The last piece of information is of a &quot;cursory nature&quot; &#x2013; it points to the next expected user action. Like &apos;accept&apos; will set things in motion, and &#x2018;step_1&#x2019; will move to just that.</p><h2 id="modals-in-the-hotwired-context">Modals in the Hotwired context</h2><p>We will build the generic modal aided by <a href="https://hotwired.dev/?ref=cunnin.gs" rel="noreferrer">Hotwire</a> &#x2013; if this is &quot;new territory&quot; to you go study it by all means; I&apos;m pretty sure you&apos;ll like it!</p><p>We will need somewhere to place the modal content so our first test must verify that we do allow for the content to be &apos;placeable&apos;</p><pre><code class="language-ruby">require &quot;application_system_test_case&quot;

class ModalTest &lt; ApplicationSystemTestCase
  setup do
    @tenant = tenants(:one)
    @user = users(:superadmin)
  end

  test &quot;visiting the index&quot; do
    login_as(@user)
    visit root_url
    # assert &lt;remote-modal-container&gt;&lt;/remote-modal-container&gt;
    assert_match &quot;remote-modal-container&quot;, page.html
  end
end
</code></pre><figure class="kg-card kg-image-card"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-14-kl.-08.05.04.png" class="kg-image" alt="The Generic Modal" loading="lazy" width="961" height="497" srcset="https://cunnin.gs/content/images/size/w600/2025/05/Sk-rmbillede-2025-05-14-kl.-08.05.04.png 600w, https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-14-kl.-08.05.04.png 961w" sizes="(min-width: 720px) 720px"></figure><p>This custom tag &lt;remote-modal-container&gt; will allow us to inject HTML into the DOM using Turbo.</p><p>From some link that you&apos;d like to open a modal - fx confirm a delete action - we would like to inject HTML. Turbo will &apos;by default&apos; <code>fetch</code> ie use AJAX to request the endpoint / the HTML and inject it into the DOM.</p><p>The link could look like this</p><pre><code class="language-ruby"># views/components/context_menu.rb
class Contextmenu &lt; Phlex::HTML
  include Phlex::Rails::Helpers::Request
  include Phlex::Rails::Helpers::Routes
  include Phlex::Rails::Helpers::LinkTo
  include Phlex::Rails::Helpers::ButtonTo
  include Rails.application.routes.url_helpers

  attr_accessor :resource, :resource_class, :list

  def initialize(resource: nil, list: nil, resource_class: nil, turbo_frame: &quot;_top&quot;, alter: true, links: [], cls: &quot;relative flex&quot;)
    @resource = resource
    @resource_class = resource_class || resource.class
    @list = list
    @turbo_frame = turbo_frame
    @alter = alter
    @links = links
    @css = cls
  end

  def view_template
    div(data_controller: &quot;contextmenu&quot;, class: @css) do
      contextmenu_button
      case true
      when !list.nil?; list_dropdown
      when !resource.nil?; dropdown
      end
    end
  end

...
      resource_class.any? ?
        link2(url: helpers.new_modal_url(
          all: true,
          modal_flavor: &quot;prompt&quot;,
          modal_form: &quot;delete&quot;,
          modal_next_step: &quot;accept&quot;,
          resource_class: resource_class.to_s.underscore,
          search: request.query_parameters.dig(:search)),
          action: &quot;click-&gt;contextmenu#hide&quot;,
          icon: &quot;trash&quot;,
          label: I18n.t(&quot;.delete_all&quot;)) :
        div(class: &quot;disabled_context_link&quot;) { I18n.t(&quot;.delete_all&quot;) }
...


    def link2(url:, 
      label:, 
      action: nil, 
      data: { turbo_stream: true }, 
      icon: nil, 
      css: &quot;context_link&quot;)
      
      data[:action] = action if action
      link_to url,
        data: data,
        class: css,
        role: &quot;menuitem&quot;,
        tabindex: &quot;-1&quot; do
        render_icon icon
        span(class: &quot;text-nowrap pl-2&quot;) { label }
        span(class: &quot;sr-only&quot;) do
          plain label
          plain &quot; &quot;
          plain resource.name rescue &quot;&quot;
        end
      end
    end


    def render_icon(icon)
      return if icon.blank?
      render &quot;Icons::#{icon.camelcase}&quot;.constantize.new(cls: &quot;contexticon&quot;)
    end

        </code></pre><p>If you&apos;d like to read about using view components leave a comment &#x1F604;</p><p>The &apos;soup&apos; to emerge from all of this is something a lot like this</p><figure class="kg-card kg-image-card"><img src="https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-13-kl.-15.11.14.png" class="kg-image" alt="The Generic Modal" loading="lazy" width="1253" height="524" srcset="https://cunnin.gs/content/images/size/w600/2025/05/Sk-rmbillede-2025-05-13-kl.-15.11.14.png 600w, https://cunnin.gs/content/images/size/w1000/2025/05/Sk-rmbillede-2025-05-13-kl.-15.11.14.png 1000w, https://cunnin.gs/content/images/2025/05/Sk-rmbillede-2025-05-13-kl.-15.11.14.png 1253w" sizes="(min-width: 720px) 720px"></figure><p>So, when a user tap&apos;s the [delete all] context menu item Turbo will send a request that looks like this (with &apos;as TURBO_STREAM&apos; being one of the essential things to note):</p><pre><code class="language-bash">15:16:32 web.1  | Started GET &quot;/modal/new?
  all=true&amp;
  modal_flavor=prompt&amp;
  modal_form=delete&amp;
  modal_next_step=accept&amp;
  resource_class=time_material&quot; for 127.0.0.1...
15:16:32 web.1  | Processing by ModalController#new as TURBO_STREAM
15:16:32 web.1  |   Parameters: {
  &quot;all&quot;=&gt;&quot;true&quot;, 
  &quot;modal_flavor&quot;=&gt;&quot;prompt&quot;,
  &quot;modal_form&quot;=&gt;&quot;delete&quot;, 
  &quot;modal_next_step&quot;=&gt;&quot;accept&quot;, 
  &quot;resource_class&quot;=&gt;&quot;time_material&quot;}</code></pre><p>So next up we&apos;ll need a controller to handle the(se) endpoint(s) - again if you have questions regarding the before_actions especially the <em>resources </em> drop me a comment below</p><pre><code class="language-ruby"># controllers/modal_controller.rb
class ModalController &lt; MortimerController
  before_action :set_vars
  before_action :set_batch
  before_action :set_filter
  before_action :set_resources
  before_action :set_resources_stream

  def new
    # resource
    @resource = find_resource
    @ids = @filter.filter != {} ||
      @batch&amp;.batch_set? || 
      @search.present? ? 
      resources.pluck(:id) : 
      []
      
  end
  
  private

    def set_vars
      @all = params[:all] || &quot;false&quot;
      @modal_form = params[:modal_form]
      @modal_flavor = params[:modal_flavor]
      @modal_next_step = params[:modal_next_step] || &quot;accept&quot; rescue &quot;&quot;
      @url = params[:url] || resources_url rescue root_url
      @search = params[:search] || &quot;&quot;
    end
</code></pre><p>The view part of things is where the Turbo really shines. We start by adding a helper to ease the repetitive task affording us a method <code>show_remote_modal</code></p><pre><code class="language-ruby"># helpers/turbo_stream_actions_helper.rb
module TurboStreamActionsHelper  
  def show_remote_modal(&amp;block)    
    turbo_stream_action_tag(:show_remote_modal,
      template: @view_context.capture(&amp;block)    
    )  
  end
end
Turbo::Streams::TagBuilder.prepend(TurboStreamActionsHelper)
</code></pre><p>That method comes to good use in the &apos;new&apos; view template like this</p><pre><code class="language-ruby">&lt;!-- views/modal/new.turbo_stream.erb --&gt;
&lt;%= turbo_stream.show_remote_modal do %&gt;
  &lt;dialog 
    id=&quot;new_form_modal&quot; 
    aria-labelledby=&quot;modal_title&quot; 
    data-controller=&quot;modal&quot; 
    data-action=&quot;keydown-&gt;modal#keydown&quot;&gt;

    &lt;!-- take your modal of choice from tailwindui.com --&gt;
    &lt;div class=&quot;tailwindclasses&quot;&gt;
      &lt;div 
        class=&quot;more-tailwind-classes&quot;&gt;
        &lt;div class=&quot;lots-of-classes&quot;&gt;
          &lt;div class=&quot;absolute right-0 top-0 pr-4 pt-4 sm:block&quot;&gt;
            &lt;form 
              id=&quot;dialog_form&quot; 
              method=&quot;dialog&quot;&gt;
              &lt;button 
                aria-label=&quot;close&quot; 
                type=&quot;submit&quot; 
                formmethod=&quot;dialog&quot; 
                data-action=&quot;click-&gt;modal#close&quot; 
                class=&quot;close_modal_button&quot;&gt;
                &lt;span class=&quot;sr-only&quot;&gt;Close&lt;/span&gt;
                &lt;svg 
                  class=&quot;h-6 w-6&quot; 
                  fill=&quot;none&quot; 
                  viewBox=&quot;0 0 24 24&quot; stroke-width=&quot;1.5&quot;
                  stroke=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;
                  &lt;path 
                    stroke-linecap=&quot;round&quot; 
                    stroke-linejoin=&quot;round&quot; 
                    d=&quot;M6 18L18 6M6 6l12 12&quot; /&gt;
                &lt;/svg&gt;
              &lt;/button&gt;
            &lt;/form&gt;
          &lt;/div&gt;
          &lt;div 
            id=&quot;modal_container&quot; 
            data-action=&quot;modalClose@window-&gt;modal#closeDialog&quot; 
            data-dialog=&quot;new_form_modal&quot;&gt;
            
            &lt;%= render @modal_form %&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/dialog&gt;
&lt;% end %&gt;
</code></pre><h4 id="stimulusthe-missing-link">Stimulus - the missing link</h4><p>It&apos;s not missing per se, more like untouched until now &#x1F600;</p><p>Hotwire packs a good punch - with Turbo controlling the navigation and loading of resources, and Stimulus allowing for custom building actions. Read more <a href="https://hotwired.dev/?ref=cunnin.gs" rel="noreferrer">about Stimulus</a>.</p><p>We connect a javascript controller to a DOM element - the <code>dialog</code> element in this case:</p><pre><code class="language-javascript">// javascript/controllers/modal_controller.js

import { Controller } from &quot;@hotwired/stimulus&quot;

// Connects to data-controller=&quot;modal&quot;
export default class extends Controller {
  static targets = [&quot;backdrop&quot;, &quot;dialog&quot;, &quot;dismiss&quot;, &quot;submitForm&quot;];

  connect() {
    document.documentElement.classList.add(&apos;lock-scroll&apos;);
    document.body.classList.add(&apos;lock-scroll&apos;);
  }

  disconnect() {
    this.removeScrollBlock()
  }

  keydown(e) {
    e.stopPropagation();
    switch(e.key) {
      case &quot;Escape&quot;:
        this.close(e);
        break;
      case &quot;Enter&quot;:
        this.submitFormTarget.click();
        break;
    }
  }

  removeScrollBlock(){
    document.documentElement.classList.remove(&quot;lock-scroll&quot;);
    document.body.classList.remove(&quot;lock-scroll&quot;);
  }

  submitForm(event) {
    this.removeScrollBlock()
  }

  show(event) {
    const dialog = document.getElementById(event.params.dialog);
    dialog.showModal();
  }  

  closeDialog(event) {
    const dialog = document.getElementById(event.target.data.dialog);
    dialog.close();
  }

  closeModal(e) {
    const modal = document.getElementById(&quot;new_form_modal&quot;);
    modal.close();
  }

  close(e) {
    this.removeScrollBlock()
  }
}</code></pre><p>The modal is, by now, entirely generic and we can throw (almost) anything in there. In the link we decide by setting a @modal_form and a @flavor. The _new partial looks like this:</p><pre><code class="language-ruby">&lt;% url = resource.id.present? ?
  modal_url(resource) : 
  modal_url(resource_class.first) %&gt;
&lt;%= form_with(url: url, method: :delete, multipart: true) do |form| %&gt;
  &lt;div class=&quot;sm:flex sm:items-start&quot;&gt;
    &lt;div class=&quot;tailwind-classes-galore&quot;&gt;
      &lt;%= render Icons::Trash.new cls: &quot;h-6 text-red-500&quot; %&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left&quot;&gt;
      &lt;h3 
        class=&quot;text-base font-semibold leading-6 text-gray-900&quot;
        id=&quot;modal_title&quot;&gt;
        
        &lt;%= delete_modal_title %&gt;
      &lt;/h3&gt;
      &lt;div class=&quot;mt-2&quot;&gt;
        &lt;p class=&quot;text-sm text-gray-500 pb-3&quot;&gt;
          &lt;%== delete_modal_instruction %&gt;&lt;/p&gt;
          &lt;%= form.hidden_field :all, value: set_all_true %&gt;
          &lt;%= form.hidden_field :modal_form, value: @modal_form %&gt;
          &lt;%= form.hidden_field :modal_next_step, value: @modal_next_step %&gt;
          &lt;%= form.hidden_field :modal_flavor, value: @modal_flavor %&gt;
          &lt;%= form.hidden_field :resource_class, value: resource_class %&gt;
          &lt;%= form.hidden_field :id, value: resource&amp;.id %&gt;
          &lt;%= form.hidden_field :search, value: params.dig(:search) %&gt;
          &lt;%= form.hidden_field :url, value: @url %&gt;
        &lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;mt-5 sm:mt-4 flex justify-between flex-row-reverse&quot;&gt;
    &lt;%= form.button
      t(&quot;#resource_class.to_s.underscore}.modal.delete.button&quot;), 
      data: { 
        modal_target: &quot;submitForm&quot;, 
        action: &quot;modal#submitForm&quot;
      }, 
      class: &quot;mort-btn-alert sm:ml-3&quot; %&gt;
      
    &lt;%= button_tag t(:cancel), 
      form: &quot;dialog_form&quot;, 
      formmethod: :dialog, 
      data: { action: &quot;click-&gt;modal#close&quot; }, 
      class: &quot;mort-btn-cancel&quot; %&gt;
  &lt;/div&gt;
&lt;% end %&gt;
</code></pre><p>This &apos;model_form&apos; sets all the variables for the form and when submitted returns a request like this:</p><pre><code class="language-bash">17:14:07 web.1  | Started DELETE &quot;/modal/388&quot; for ...
17:14:07 web.1  | Processing by ModalController#destroy as TURBO_STREAM
17:14:07 web.1  |   Parameters: {&quot;authenticity_token&quot;=&gt;&quot;[FILTERED]&quot;,
  &quot;modal_flavor&quot;=&gt;&quot;prompt&quot;, 
  &quot;modal_form&quot;=&gt;&quot;delete&quot;, 
  &quot;modal_next_step&quot;=&gt;&quot;accept&quot;, 
  &quot;resource_class&quot;=&gt;&quot;TimeMaterial&quot;, 
  &quot;all&quot;=&gt;&quot;true&quot;, 
  &quot;attachment&quot;=&gt;&quot;&quot;, 
  &quot;id&quot;=&gt;&quot;388&quot;, 
  &quot;search&quot;=&gt;&quot;&quot;, 
  &quot;url&quot;=&gt;&quot;https://localhost:3000/modal&quot;, 
  &quot;button&quot;=&gt;&quot;&quot;}</code></pre><p>Back in the controller we handle this like so:</p><pre><code class="language-ruby"># controllers/modal_controller.rb
class ModalController &lt; MortimerController
  def destroy
    params[:action] = &quot;destroy&quot;
    params[:all] == &quot;true&quot; ? process_destroy_all : process_destroy
  end


  private
 

    def process_destroy_all
      begin
        DeleteAllJob.perform_later 
          tenant: Current.tenant, 
          user: Current.user, 
          resource_class: resource_class.to_s,
          ids: resources.pluck(:id),
          batch: @batch,
          user_ids: (
            resource_class.first.respond_to?(:user_id) ?
            resources.pluck(:user_id).uniq : 
            User.by_tenant.by_role(:user).pluck(:id)) rescue nil
            
        @url.gsub!(/\/\d+$/, &quot;&quot;) if @url.match?(/\d+$/)
        flash[:success] = t(&quot;delete_all_later&quot;)
        respond_to do |format|
          format.turbo_stream { }
          format.html { 
            redirect_to @url, 
            status: 303, 
            success: t(&quot;delete_all_later&quot;) 
          }
          format.json { head :no_content }
        end
      rescue =&gt; e
        say &quot;ERROR on destroy: #{e.message}&quot;
        redirect_to 
          resources_url, status: 303, error: t(&quot;something_went_wrong&quot;)
      end
    end
 
end</code></pre><p>I&apos;ll have to save building the <code>guide</code> flavored modal for another day &#x2013; but here you have it: a generic modal ready for display just about anything you can think of.</p><p>Enjoy</p>]]></content:encoded></item><item><title><![CDATA[Building Mortimer, Day 7]]></title><description><![CDATA[<p></p><h2 id="day-7">Day 7</h2><p>I was a full week into building the next volume of Mortimer and I was so deep into building, with <a href="https://cunnin.gs/building-mortimer-day-6/" rel="noreferrer">day 6</a> being a huge day, that I did not see it coming - stay tuned for the sausy details!</p><p>I had to stop the <code>debug</code> first thing</p>]]></description><link>https://cunnin.gs/building-mortimer-day-7/</link><guid isPermaLink="false">66eac58a97b4a40007d58202</guid><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Thu, 10 Apr 2025 10:23:47 GMT</pubDate><content:encoded><![CDATA[<p></p><h2 id="day-7">Day 7</h2><p>I was a full week into building the next volume of Mortimer and I was so deep into building, with <a href="https://cunnin.gs/building-mortimer-day-6/" rel="noreferrer">day 6</a> being a huge day, that I did not see it coming - stay tuned for the sausy details!</p><p>I had to stop the <code>debug</code> first thing this morning &#x2013; and once I was ready to resume work, this is what greeted me!</p><p><code>Address already in use - bind(2) for &quot;0.0.0.0&quot; port 3000 </code> </p><p>somehow I cannot start <code>dev</code> or <code>debug</code> and the scary part is - I don&#x2019;t know why!</p><p>I opened Activity Monitor and started sniffing around. Turns out it was this bad boy hiding in the <a href="https://www.imdb.com/title/tt0120762/characters/nm0000552?ref=cunnin.gs" rel="noreferrer">pot-pourri</a></p><pre><code class="language-ruby">14745   ??  S      0:16.89 solid-queue-supervisor(0.9.0): supervising 14774, 14775, 14776 
</code></pre><p>lingering behind <em>the curtains</em> not making it easy on me guessing what was really going on.<br>I <code>kill -HUP 14745</code> and that was the end of it! Ohh - and the 14745 is the PID which most certainly will never match your case!</p><h3 id="landing-page">Landing page</h3><p>Once I got <em>in gear</em> I spent a good half day transposing </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://pocket.tailwindui.com/?ref=cunnin.gs"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Pocket - Invest at the perfect time.</div><div class="kg-bookmark-description">By leveraging insights from our network of industry insiders, you&#x2019;ll know exactly when to buy to maximize profit, and exactly when to sell to avoid painful losses.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://pocket.tailwindui.com/favicon.ico" alt><span class="kg-bookmark-author">Invest at the perfect time.</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://pocket.tailwindui.com/_next/static/media/phone-frame.d4b6b62a.svg" alt></div></a></figure><p>into the landing page for Mortimer.</p><h2 id="stopping-the-blue-box-volume">Stopping the blue-box volume!</h2><p>How the h***! I mean, I was just about to getting some real traction here and then this final blow!</p><p>Remember back a week ago when we kind&apos;a lost most of our expected user base? With only one potential customer left &#x2013; and now they gave up on us too. So even if we did pivot it was not going to be enough!</p><p>So the story could have ended here but as I am adding this paragraph almost 7 months down the road, it did not. We pivoted some more - a lot in fact. Only time will tell if we did the right thing!</p><p>This series ends here &#x2013; but I&apos;ll start compiling a new one detailing the <a href="https://en.wikipedia.org/wiki/Interregnum?ref=cunnin.gs" rel="noreferrer">interregnum</a> in a short while.</p>]]></content:encoded></item><item><title><![CDATA[How to get paid]]></title><description><![CDATA[Using Stripe to collect subscription fees from your customers is incredibly easy -- Stripe did such a good job documenting the process, and holding your hand along the way with their setup guide!]]></description><link>https://cunnin.gs/how-to-get-paid/</link><guid isPermaLink="false">67ecc78e029b6e0007774fb0</guid><category><![CDATA[rails]]></category><category><![CDATA[ruby]]></category><category><![CDATA[payment processor]]></category><dc:creator><![CDATA[Walther H Diechmann]]></dc:creator><pubDate>Mon, 07 Apr 2025 22:58:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1637169797848-12431f1d355c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE3fHxwYXl8ZW58MHx8fHwxNzQ0MDY2NDM2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1637169797848-12431f1d355c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE3fHxwYXl8ZW58MHx8fHwxNzQ0MDY2NDM2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to get paid"><p>Got a great product and now you would like your consumers to start paying for it! Doesn&apos;t sound too far fetched and quite reasonable in fact &#x2013; but how does the mechanics work at all? Like, where do I sign to make this happen? If you are like I was, right now you got a lot of questions!</p><p>This post is about how you go from running/providing a service to see money getting into your bank account.</p><blockquote>Disclaimer: I did not receive any kick backs or advertisement benefits for doing this post nor am I an affiliate partner of any service provider mentioned in this post!</blockquote><h2 id="who-will-collect-the-money-on-my-behalf">Who will collect the money on my behalf?</h2><p>One of my first questions was just that! I knew I (designed an inter-National Banks cheque exchange system back in the early 90&apos;s, so I probably) could build an integration with a bank &#x2013; a number of banks to be more precise &#x2013; using the <a href="https://eur-lex.europa.eu/eli/dir/2015/2366/oj/eng?ref=cunnin.gs" rel="noreferrer">PSD2</a> (EU 2015/2366 Directive) but that would require me to handle a lot of regulation and compliance myself!</p><p>An easier (and a lot faster) way is to pay someone else to do that for me just like I plan to get paid by my customers to do something for them. A payment gateway that is called. The one that comes to mind (and mentioned a lot when you google it) obviously is Stripe. There are many like Stripe and some that are less costly but one reason to go with Stripe is the api documentation which Stripe has taken to a whole different level! &#x1F973;</p><h2 id="where-to-start">Where to start?</h2><p>There are a number of issues to tick off and I made a list for your benefit:</p><ul><li>get a bank account</li><li>setup an account with Stripe</li><li>install the &apos;stripe&apos; gem</li><li>generate the payment link</li><li>add callback route </li></ul><p>The rest of this post will go through each issue and solve them.</p><h3 id="get-a-bank-account">Get a bank account</h3><p>This may sound crazy but even in 2025 you still need to have one of these archaic constructs: a bank account! I have a lot to say about that but let&apos;s focus here. </p><p>You need a bank account that will do IBAN/SWIFT payments (that&apos;s international money wiring). I&apos;m doing this from Denmark so I&apos;m not sure if you can get away with just your Apple Pay account in the US but anyways...</p><h3 id="an-account-with-stripe">An account with Stripe</h3><p>Setting up an account with Stripe is fairly easy. Go to <a href="https://stripe.com/?ref=cunnin.gs" rel="noreferrer">stripe.com</a> and sign up and then start setting up your account by following a very crafty made setup guide (thank you Stripe - job well done)</p><figure class="kg-card kg-image-card"><img src="https://cunnin.gs/content/images/2025/04/Sk-rmbillede-2025-04-04-kl.-14.37.09.png" class="kg-image" alt="How to get paid" loading="lazy" width="749" height="365" srcset="https://cunnin.gs/content/images/size/w600/2025/04/Sk-rmbillede-2025-04-04-kl.-14.37.09.png 600w, https://cunnin.gs/content/images/2025/04/Sk-rmbillede-2025-04-04-kl.-14.37.09.png 749w" sizes="(min-width: 720px) 720px"></figure><h3 id="install-the-stripe-gem">Install the &apos;stripe&apos; gem</h3><p>This one was easy as pie &#x2013; I just ran <code>bundle add stripe</code> and almost was done!</p><h3 id="generate-the-payment-link">Generate the Payment Link</h3><p>From somewhere, probably the UI, you&apos;ll call the generation of the Payment Link and you can make it as customized and fancy as you like (there is a ton of API calls to that end) or you can lock in on a select few things, like a product, a price and boom. Once generated you&apos;ll show that to the user somehow - a modal, an email, whatever - and then just sit tight and wait for the callback &#x1F606;</p><h3 id="add-callback-route">Add callback route</h3><p>You could argue that a callback route is of less value in a workflow like this one &#x2013; being variably asynchronous; but it actually makes a lot of sense &#x2013; if you take the same <em>route</em> I did, that is!</p><p>I use the Payment Link API to generate a unique link to every new customer (a user that selects one of the payable services to choose from in <a href="https://mortimer.pro/?ref=cunnin.gs" rel="noreferrer">Mortimer</a>) and this link may live (forever?) a long time, until the user decides to use it. This way I get to do this, eventually/hopefully always:</p><pre><code class="language-ruby">
class Stripe::PaymentController &lt; ApplicationController
  # Stripe callback from completed payment
  # GET &quot;/stripe/payment?ci=cs_test_a1M01tZueaLHFtv...Cwn5tgV61&quot;
  #
  # param 
  #   ui = user_id
  #   ci = checkout_session_id
  #
  def new
    Current.system_user = User.find(params[&quot;ui&quot;])
    ss = Stripe::Service.new user: Current.get_user
    if ss.confirm_payment(params[&quot;ci&quot;])
      redirect_to edit_tenant_url(Current.get_tenant), 
        success: t(&quot;payment_successfully_confirmed&quot;)
    else
      redirect_to edit_tenant_url(Current.get_tenant), 
        warning: t(&quot;payment_not_confirmed&quot;)
    end
  end
end

</code></pre>
<h3 id="done">Done</h3><p>That&apos;s about it actually &#x2013; you&apos;ll want to keep stuff together (I keep stuff in a folder labeled &apos;services&apos; and below that I have a &apos;stripe&apos; folder with a class like this </p><pre><code class="language-ruby">class Stripe::Service &lt; SaasService

  def payment_link url, price
    Stripe.api_key = settings[&quot;api_key&quot;]
    pl = Stripe::PaymentLink.create({
      line_items: [
        {
          price: price.id,
          quantity: 1
        }
      ],
      after_completion: {
        type: &quot;redirect&quot;,
        redirect: {
          url: url
        }
      }
    })
    return pl.url if pl.active
    false
  end
  
  def confirm_payment checkout_session_id
    Stripe.api_key = settings[&quot;api_key&quot;]
    scs = Stripe::Checkout::Session.retrieve checkout_session_id
    if scs[&quot;payment_status&quot;] == &quot;paid&quot;
      invoice = Stripe::Invoice.retrieve scs[&quot;invoice&quot;]
      prod = Stripe::Product.retrieve(invoice
        .lines.data.first.pricing.price_details.product)
      if prod.present?
        case prod.name
        when /pro/; Current.get_tenant.update license: 3
        when /essential/; Current.get_tenant.update license: 2
        end
      end

      TenantMailer.with(tenant: Current.get_tenant, 
        user: Current.get_user, 
        recipient: Current.get_tenant.email, 
        invoice_pdf: invoice.invoice_pdf)
        .send_invoice.deliver_later
      Current.get_tenant.update license_changed_at: Time.current,
        license_expires_at: Time.at(invoice
          .lines.data.first.period.end).utc
      true
    else
      false
    end
  end
</code></pre>
<p>but that&apos;s just smoke and mirrors &#x1F609; - the Stripe gem does all the heavy lifting, really!</p>]]></content:encoded></item></channel></rss>