<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6768017463404896606</id><updated>2026-09-08T20:57:00.769+08:00</updated><category term="python"/><category term="django"/><category term="hosting"/><category term="vps"/><category term="aws"/><category term="drupal"/><category term="php"/><category term="apache"/><category term="buildout"/><category term="google"/><category term="http"/><category term="linux"/><category term="nginx"/><category term="plesk"/><category term="samsung-e2230m"/><category term="selenium"/><category term="telnet"/><category term="testing"/><category term="twill"/><category term="ubuntu"/><category term="3g"/><category term="ai"/><category term="ajenti"/><category term="android"/><category term="bitcoin"/><category term="blockchain"/><category term="bluetooth"/><category term="cpanel"/><category term="cse"/><category term="decentralized"/><category term="dev"/><category term="dhcp"/><category term="dns"/><category term="dnsmasq"/><category term="docker"/><category term="ebookme"/><category term="ec2"/><category term="editor"/><category term="email"/><category term="flunc"/><category term="git"/><category term="golang"/><category term="gprs"/><category term="headless"/><category term="irc"/><category term="java"/><category term="jinja"/><category term="lightsail"/><category term="lxml"/><category term="materialized path"/><category term="midp"/><category term="mysql"/><category term="nc"/><category term="open-source"/><category term="p2p"/><category term="postgresql"/><category term="pycon"/><category term="pylons"/><category term="qmail"/><category term="ses"/><category term="singapore"/><category term="smart-contract"/><category term="smtp"/><category term="sns"/><category term="time"/><category term="tree"/><category term="unicode"/><category term="unit"/><category term="virtualbox"/><category term="wayland"/><category term="xvfb"/><title type='text'>Meta k4mL</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>54</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-1719075362299521544</id><published>2026-01-11T09:41:00.000+08:00</published><updated>2026-01-11T09:41:11.271+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ai"/><title type='text'>AI coding tools fragmentation</title><content type='html'>&lt;div _ngcontent-ng-c1183142467=&quot;&quot; aria-busy=&quot;false&quot; aria-live=&quot;off&quot; class=&quot;markdown markdown-main-panel enable-updated-hr-color&quot; dir=&quot;ltr&quot; id=&quot;model-response-message-contentr_8bef120d7bd9d74d&quot; style=&quot;--animation-duration: 400ms; --fade-animation-function: linear; font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;h3 data-path-to-node=&quot;0&quot; style=&quot;font-family: Google Sans, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Executive Summary&lt;/b&gt;&lt;/h3&gt;&lt;p data-path-to-node=&quot;1&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The discussion centers on a &lt;a href=&quot;https://x.com/dhh/status/2009737797263905234&quot; rel=&quot;nofollow&quot;&gt;tweet&lt;/a&gt; by DHH (David Heinemeier Hansson) from &lt;b data-index-in-node=&quot;73&quot; data-path-to-node=&quot;1&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;January 10, 2026&lt;/b&gt;, arguing against the fragmentation of AI coding tools. DHH contends that developers do not want a separate CLI for every model provider (e.g., Anthropic’s Claude Code, OpenAI’s tools). Instead, he advocates for a &lt;b data-index-in-node=&quot;303&quot; data-path-to-node=&quot;1&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;unified interface&lt;/b&gt;—specifically citing &lt;b data-index-in-node=&quot;341&quot; data-path-to-node=&quot;1&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;OpenCode&lt;/b&gt;—that allows developers to swap models within a single environment.&lt;/p&gt;&lt;p data-path-to-node=&quot;2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The thread reveals a three-way tension in the developer community between &lt;b data-index-in-node=&quot;74&quot; data-path-to-node=&quot;2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;convenience&lt;/b&gt; (unified tools), &lt;b data-index-in-node=&quot;103&quot; data-path-to-node=&quot;2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;corporate control&lt;/b&gt; (walled gardens), and &lt;b data-index-in-node=&quot;143&quot; data-path-to-node=&quot;2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;sovereignty&lt;/b&gt; (running local models).&lt;/p&gt;&lt;hr data-path-to-node=&quot;3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot; /&gt;&lt;h3 data-path-to-node=&quot;4&quot; style=&quot;font-family: Google Sans, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Key Discussion Themes&lt;/b&gt;&lt;/h3&gt;&lt;h4 data-path-to-node=&quot;5&quot; style=&quot;font-family: Google Sans, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;1. The Fatigue of Fragmentation&lt;/b&gt;&lt;/h4&gt;&lt;ul data-path-to-node=&quot;6&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;6,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The Problem:&lt;/b&gt; Developers are exhausted by &quot;model choice fatigue&quot; and the need to manage multiple CLIs. As &lt;b data-index-in-node=&quot;105&quot; data-path-to-node=&quot;6,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Rob Zolkos&lt;/b&gt; noted, the fragmentation is absurd, leading some to write scripts just to manage their AI CLIs.&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;6,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The Desire:&lt;/b&gt; There is a strong consensus (DHH, Will McGugan, Mustafa Ergisi) that a &quot;universal shell&quot; or single interface is necessary for sanity.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4 data-path-to-node=&quot;7&quot; style=&quot;font-family: Google Sans, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;2. Aggregators vs. Walled Gardens&lt;/b&gt;&lt;/h4&gt;&lt;ul data-path-to-node=&quot;8&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;8,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The Conflict:&lt;/b&gt; Users want to bring their existing subscriptions to unified tools, but providers are fighting back. &lt;b data-index-in-node=&quot;114&quot; data-path-to-node=&quot;8,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Josh Whiton&lt;/b&gt; highlighted a major friction point: Anthropic reportedly blocked the use of his subscription within OpenCode, forcing him to pay via API instead. This suggests AI providers are trying to force users into their native ecosystems.&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;8,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;/span&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The Alternatives:&lt;/span&gt;&lt;/b&gt;&lt;span class=&quot;citation-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt; &lt;/span&gt;&lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;8,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Riccardo Spagni&lt;/span&gt;&lt;/b&gt;&lt;span class=&quot;citation-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt; mentioned &lt;/span&gt;&lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;8,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;AmpCode&lt;/span&gt;&lt;/b&gt;&lt;span class=&quot;citation-3 citation-end-3&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;, a tool that goes a step further by abstracting model selection entirely and offering free usage of high-end models (Opus 4.5), suggesting a market shift toward tools that handle the &quot;thinking&quot; about which model to use.&lt;sup _ngcontent-ng-c1679178058=&quot;&quot; class=&quot;superscript&quot; data-turn-source-index=&quot;1&quot; style=&quot;background-color: transparent !important; font-family: Google Sans Text, sans-serif !important; font-size: 16px !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;1&lt;/sup&gt;&lt;/span&gt;&lt;/p&gt;&lt;div _ngcontent-ng-c2359566075=&quot;&quot; class=&quot;source-inline-chip-container ng-star-inserted&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;button _ngcontent-ng-c2359566075=&quot;&quot; aria-label=&quot;View source details for citation from Hamel Husain. Opens side panel.&quot; class=&quot;button ng-star-inserted&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4 data-path-to-node=&quot;9&quot; style=&quot;font-family: Google Sans, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;3. Local Sovereignty &amp;amp; Security&lt;/b&gt;&lt;/h4&gt;&lt;ul data-path-to-node=&quot;10&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;10,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The &quot;Sovereign&quot; Stance:&lt;/b&gt; &lt;b data-index-in-node=&quot;24&quot; data-path-to-node=&quot;10,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Ahmad&lt;/b&gt; argued strongly for &quot;intelligence sovereignty.&quot; He abandoned Claude Code for OpenCode not just for convenience, but to use open-source models (GLM-4.7, MiniMax-M2.1) hosted locally. His argument is that relying on cloud providers allows them to &quot;nerf&quot; or &quot;rug pull&quot; your intelligence.&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;10,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;/span&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Security Concerns:&lt;/span&gt;&lt;/b&gt;&lt;span class=&quot;citation-2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt; &lt;/span&gt;&lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;10,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;span class=&quot;citation-2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Alex&lt;/span&gt;&lt;/b&gt;&lt;span class=&quot;citation-2 citation-end-2&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt; raised concerns about AI agents having permission to delete hard drives.&lt;sup _ngcontent-ng-c1679178058=&quot;&quot; class=&quot;superscript&quot; data-turn-source-index=&quot;2&quot; style=&quot;background-color: transparent !important; font-family: Google Sans Text, sans-serif !important; font-size: 16px !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;2&lt;/sup&gt;&lt;/span&gt; DHH countered this with a &quot;stateless&quot; philosophy (&quot;No backup, no cry&quot;), claiming he treats machines as disposable and relies on OpenCode’s file access guardrails.&lt;/p&gt;&lt;div _ngcontent-ng-c2359566075=&quot;&quot; class=&quot;source-inline-chip-container ng-star-inserted&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;button _ngcontent-ng-c2359566075=&quot;&quot; aria-label=&quot;View source details for citation from YouTube. Opens side panel.&quot; class=&quot;button ng-star-inserted&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;11&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot; /&gt;&lt;h3 data-path-to-node=&quot;12&quot; style=&quot;font-family: Google Sans, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Most Valuable Insight: The &quot;Interface War&quot; has begun&lt;/b&gt;&lt;/h3&gt;&lt;p data-path-to-node=&quot;13&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;The most critical insight from this thread is that &lt;b data-index-in-node=&quot;51&quot; data-path-to-node=&quot;13&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;the primary battleground for AI coding agents has shifted from &quot;Who has the best model?&quot; to &quot;Who owns the developer workflow?&quot;&lt;/b&gt;&lt;/p&gt;&lt;p data-path-to-node=&quot;14&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;While developers theoretically want &quot;one tool for all models,&quot; the underlying conflict is economic and structural:&lt;/p&gt;&lt;ul data-path-to-node=&quot;15&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;15,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Providers (e.g., Anthropic)&lt;/b&gt; want to own the interface to protect their recurring revenue (subscriptions) and prevent commoditization.&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;15,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Aggregators (e.g., OpenCode)&lt;/b&gt; want to commoditize the models, turning them into interchangeable back-ends for their superior UX.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-bottom: 0px; padding-top: 0px;&quot;&gt;The Insight:&lt;/p&gt;&lt;p style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-bottom: 0px; padding-top: 0px;&quot;&gt;The winning tools won&#39;t just be &quot;CLIs that call models.&quot; They will be Abstraction Layers (like AmpCode or OpenCode) that either:&lt;/p&gt;&lt;ol data-path-to-node=&quot;17&quot; start=&quot;1&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;17,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Automate Model Selection:&lt;/b&gt; Removing the need for the dev to choose between &quot;Opus&quot; or &quot;GPT-6.&quot;&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;17,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Enforce Sovereignty:&lt;/b&gt; allowing seamless switching to local compute when cloud providers attempt to lock users in.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-path-to-node=&quot;18&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;As &lt;b data-index-in-node=&quot;3&quot; data-path-to-node=&quot;18&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;Josh Whiton’s&lt;/b&gt; experience proves, &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;18&quot; style=&quot;font-family: Google Sans Text, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;model providers will actively penalize users for using unified interfaces&lt;/b&gt;, meaning the future of AI coding tools will likely involve an &quot;arms race&quot; between API restrictions and open-source workarounds.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/1719075362299521544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/1719075362299521544?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1719075362299521544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1719075362299521544'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2026/01/ai-coding-tools-fragmentation.html' title='AI coding tools fragmentation'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-2995434601172939780</id><published>2025-03-12T14:11:00.000+08:00</published><updated>2025-03-12T14:11:10.383+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><category scheme="http://www.blogger.com/atom/ns#" term="wayland"/><title type='text'>Ubuntu 22.04 Wayland share screen</title><content type='html'>&lt;p&gt;&amp;nbsp;After switching to Dell XPS 13 which running Ubuntu 22.04, I noticed that trying to share screen through Google Meet, it shows this:-&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkEUz39nHBye9Ql2btnxZRiUcb6-Wn7PowAw-u0EvIoA_p9ILCCMptZycSHU93flaIeVWHw1lgiq-_BC-t6CJt6kW9fCOlrGquMqjH4paJHI7wljSUpcLHUeIWSevVuvdYHrpZingzu_j_IotAqcsQsDQ_W3rkW2pJlVkUnLX1I0J7Lm77TtUfmh4o9Gnm/s692/Screenshot%20from%202025-03-12%2013-54-29.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;303&quot; data-original-width=&quot;692&quot; height=&quot;140&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkEUz39nHBye9Ql2btnxZRiUcb6-Wn7PowAw-u0EvIoA_p9ILCCMptZycSHU93flaIeVWHw1lgiq-_BC-t6CJt6kW9fCOlrGquMqjH4paJHI7wljSUpcLHUeIWSevVuvdYHrpZingzu_j_IotAqcsQsDQ_W3rkW2pJlVkUnLX1I0J7Lm77TtUfmh4o9Gnm/s320/Screenshot%20from%202025-03-12%2013-54-29.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&amp;nbsp;This - Use operating system settings, I never saw it before. Usually here we will be presented the windows that we want to share.&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;It turned out that screen sharing in Ubuntu 22.04 indeed an issue, due to the use of Wayland instead of Xorg as its display server. Many suggested to disable wayland and back to use Xorg. I try to avoid that since Wayland seems to works fine so far.&lt;/p&gt;&lt;p&gt;After some searching, the &lt;a href=&quot;https://www.technowizardry.net/2024/09/screen-sharing-on-wayland-ubuntu-24-04/&quot;&gt;conclusion&lt;/a&gt; seems we can make this working by installing some packages.&lt;/p&gt;&lt;p&gt;sudo apt install xdg-desktop-portal xdg-desktop-portal-gnome&lt;/p&gt;&lt;p&gt;But it turned out that I have already installed the packages! So what were the problems?&lt;/p&gt;&lt;p&gt;&amp;nbsp;Well, turn out it&#39;s more psychological than technical. Since the pop up is different than what I&#39;m used to before, I never click the allow button. But clicking the allow button we will see this:-&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC035oJ9jI_FQmakFnbjKPOJ0HHO3HiEv01VWXj7JMOhYLVsbLMJMPyUls1omdgzKvj46JJ6F_VnWfPM9F7k1bfWKVBhhp5o-CP798wDDz3zpfQ1q1RqCMVXfiyJO3Xx-_OArER1JKBRtDkOlrxEW-nznrVkqbj9gk38DlNtk4yEyq3fVl7H7VxR1imAUp/s692/Screenshot%20from%202025-03-12%2013-55-08.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;411&quot; data-original-width=&quot;692&quot; height=&quot;190&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC035oJ9jI_FQmakFnbjKPOJ0HHO3HiEv01VWXj7JMOhYLVsbLMJMPyUls1omdgzKvj46JJ6F_VnWfPM9F7k1bfWKVBhhp5o-CP798wDDz3zpfQ1q1RqCMVXfiyJO3Xx-_OArER1JKBRtDkOlrxEW-nznrVkqbj9gk38DlNtk4yEyq3fVl7H7VxR1imAUp/s320/Screenshot%20from%202025-03-12%2013-55-08.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;Which is what I&#39;m looking for - a way to specify which window I want to share.&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt; &lt;br /&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/2995434601172939780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/2995434601172939780?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2995434601172939780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2995434601172939780'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2025/03/ubuntu-2204-wayland-share-screen.html' title='Ubuntu 22.04 Wayland share screen'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkEUz39nHBye9Ql2btnxZRiUcb6-Wn7PowAw-u0EvIoA_p9ILCCMptZycSHU93flaIeVWHw1lgiq-_BC-t6CJt6kW9fCOlrGquMqjH4paJHI7wljSUpcLHUeIWSevVuvdYHrpZingzu_j_IotAqcsQsDQ_W3rkW2pJlVkUnLX1I0J7Lm77TtUfmh4o9Gnm/s72-c/Screenshot%20from%202025-03-12%2013-54-29.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-3858861924214360016</id><published>2022-09-12T15:41:00.003+08:00</published><updated>2022-09-12T15:41:40.952+08:00</updated><title type='text'>Creating tag from github web UI</title><content type='html'>&lt;p&gt;&amp;nbsp;Problem: I have Github Actions that automatically create a new release when certain tag is pushed. The only way to create tag from github web interface is to create a new release. The Actions[1] I&#39;m using however failed with error message:-&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote&gt;Validation Failed: {&quot;resource&quot;:&quot;Release&quot;,&quot;code&quot;:&quot;already_exists&quot;,&quot;field&quot;:&quot;tag_name&quot;}&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;If the tag was created from release.&lt;/p&gt;&lt;p&gt;We can create the tag using another Actions, there are a number of Actions that provide that. First I try using Github Script:-&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;name: &quot;create-tag&quot;

on:
  workflow_dispatch:
    inputs:
      tagname:
        description: &#39;Enter tag&#39;
        required: true
        type: string
    
jobs:
  create_tag:
    name: &quot;Create tag&quot;
    runs-on: &quot;ubuntu-latest&quot;
    steps:
      - name: Create tag ${{ inputs.tagname }}
        uses: actions/github-script@v5
        with:
          script: |
            github.rest.git.createRef({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: &#39;refs/tags/${{ inputs.tagname }}&#39;,
              sha: context.sha
            })
&lt;/pre&gt;


&lt;p&gt;For some reason that doesn&#39;t trigger push event though. So I try &lt;span class=&quot;blob-code-inner blob-code-marker js-code-nav-pass &quot; data-code-marker=&quot;+&quot;&gt;&lt;span class=&quot;pl-s x x-first x-last&quot;&gt;rickstaa/action-create-tag@v1 but also it didn&#39;t trigger push event. This second event is just using plain `git push ...` to push the tag instead of using the REST API, so it&#39;s weird that it doesn&#39;t trigger the push event.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&lt;/span&gt;[1]: marvinpinto/action-automatic-releases@latest&lt;span class=&quot;pl-pds&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/3858861924214360016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/3858861924214360016?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/3858861924214360016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/3858861924214360016'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2022/09/creating-tag-from-github-web-ui.html' title='Creating tag from github web UI'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-8817264147871514933</id><published>2022-08-31T10:08:00.001+08:00</published><updated>2022-08-31T10:08:07.480+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="android"/><category scheme="http://www.blogger.com/atom/ns#" term="google"/><title type='text'>Google Family Link Android Device isn&#39;t ready</title><content type='html'>&lt;p&gt;&amp;nbsp;When setting up Family Link for my kid, the setup keep failing and just showing very unhelpful error message &quot;Something went wrong ..&quot;. Since the phone doesn&#39;t has family link installed yet, I try to login with my account first and installed Family Link app.&lt;/p&gt;&lt;p&gt;Opening the app, on my kid&#39;s account status, I can see it shows &quot;Device isn&#39;t ready for ...&quot;. I removed my account (since family link account can&#39;t share device) and try to add my kid&#39;s account again. This time the setup went through!&lt;/p&gt;&lt;p&gt;So if you&#39;re facing similar issue and doesn&#39;t has Family Link app installed on the device yet, try to install the app with your own account first, before setting up your kid&#39;s account.&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/8817264147871514933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/8817264147871514933?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8817264147871514933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8817264147871514933'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2022/08/google-family-link-android-device-isnt.html' title='Google Family Link Android Device isn&#39;t ready'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-1926834798867287233</id><published>2022-08-09T23:25:00.001+08:00</published><updated>2022-08-09T23:25:13.549+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dev"/><category scheme="http://www.blogger.com/atom/ns#" term="editor"/><title type='text'>VS Code editor in browser</title><content type='html'>&lt;p&gt;&amp;nbsp;There are 2 options, basically:-&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;code-server&lt;/li&gt;&lt;li&gt;OpenVSCode&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What&#39;s the difference? Excerpt from code-server&#39;s FAQ:-&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;code-server and OpenVSCode-Server both allow you to access VS Code via a
browser. The two projects also use their own &lt;a href=&quot;https://github.com/coder/vscode&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;forks of VS Code&lt;/a&gt; to
leverage modern VS Code APIs and stay up to date with the upsteam version.&lt;/p&gt;
&lt;p&gt;However, OpenVSCode-Server is scoped at only making VS Code available in the web browser.
code-server includes some other features:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;password auth&lt;/li&gt;&lt;li&gt;proxy web ports&lt;/li&gt;&lt;li&gt;certificate support&lt;/li&gt;&lt;li&gt;plugin API&lt;/li&gt;&lt;li&gt;settings sync (coming soon)&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;p&gt;&amp;nbsp;So I decided to try code-server. Installation is straightforward, as copied from the docs page:- &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;curl -fsSL https://code-server.dev/install.sh | sh&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;After that you can run it as:-&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;PORT=3000 code-server&lt;/span&gt;&lt;/p&gt;&lt;p&gt;To actually access that from my laptop, I just use ssh port forwarding:-&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;ssh -L 3000:localhost:3000 my-server&lt;/span&gt;&lt;/p&gt;&lt;p&gt;I can then access it through my browser at https://localhost:3000/. You&#39;ll need to copy password from &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;~/.config/code-server/config.yaml.&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;So far the experience is satisfying. It feels the same as in Github Codespaces.&lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/1926834798867287233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/1926834798867287233?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1926834798867287233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1926834798867287233'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2022/08/vs-code-editor-in-browser.html' title='VS Code editor in browser'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-4227739929338857933</id><published>2018-03-28T08:18:00.003+08:00</published><updated>2018-03-28T08:18:49.602+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cse"/><category scheme="http://www.blogger.com/atom/ns#" term="docker"/><category scheme="http://www.blogger.com/atom/ns#" term="google"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><title type='text'>PHP with docker</title><content type='html'>A friend asking about a PHP library and I decided to test whether that library is working. But I don&#39;t have PHP environment setup (we&#39;re Python shop btw). But thanks to docker, that&#39;s easy these days.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;docker run -it --tty --rm --volume $PWD:/app --user $(id -u):$(id -g) composer require google/apiclient:^2.0
&lt;/pre&gt;
&lt;br /&gt;
Then we just need to create the script to run, still in the same directory:-&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;include_once __DIR__ . &#39;/vendor/autoload.php&#39;;

$GCSE_API_KEY = &quot;nqwkoigrhe893utnih_gibberish_q2ihrgu9qjnr&quot;;
$GCSE_SEARCH_ENGINE_ID = &quot;937592689593725455:msi299dkne4de&quot;;

$client = new Google_Client();
$client-&amp;gt;setApplicationName(&quot;My_App&quot;);
$client-&amp;gt;setDeveloperKey($GCSE_API_KEY);
$service = new Google_Service_Customsearch($client);
$optParams = array(&quot;cx&quot;=&amp;gt;self::GCSE_SEARCH_ENGINE_ID);    
$results = $service-&amp;gt;cse-&amp;gt;listCse(&quot;lol cats&quot;, $optParams);&lt;/pre&gt;
&lt;br /&gt;
And we can run that script again using docker:-&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;docker run -it --rm --volume $PWD:/app -w /app php:5.5-cli php cse.php
&lt;/pre&gt;
&lt;br /&gt;
Links:-&lt;br /&gt;
&lt;br /&gt;
https://stackoverflow.com/questions/41592249/how-to-use-php-client-for-google-custom-search-engine&lt;br /&gt;
https://hub.docker.com/r/library/php/&lt;br /&gt;
https://hub.docker.com/r/library/composer/</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/4227739929338857933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/4227739929338857933?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/4227739929338857933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/4227739929338857933'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2018/03/php-with-docker.html' title='PHP with docker'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-869453395265260033</id><published>2018-03-13T17:47:00.001+08:00</published><updated>2018-03-13T17:47:44.173+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="aws"/><category scheme="http://www.blogger.com/atom/ns#" term="email"/><category scheme="http://www.blogger.com/atom/ns#" term="ses"/><category scheme="http://www.blogger.com/atom/ns#" term="sns"/><title type='text'>Receiving Email Using SES</title><content type='html'>There&#39;s service like Pawnmail (seem to be down now) that allow you to receive email on your custom domain. But that&#39;s a third party and mean adding another one into your trust list.&lt;br /&gt;
&lt;br /&gt;
Since we already using AWS, I&#39;m wondering if it has anything that can receive email. They have SES that usually used to send email. But turn out SES can also receive email. So just add the MX record in your DNS to&amp;nbsp;inbound-smtp.us-east-1.amazonaws.com.&lt;br /&gt;
&lt;br /&gt;
Next is to configure the domain and address to receive the email.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihaTbo_gU6qTPzwYYI-vesyKNirFU7K61H7fdsLJ1sphW1BOxe4iMOQ5ebhxQaOVjfd_UrAper1Xpmg7a3xR_Psm8hjM8aaoMlzzo6QX1rqkznS-YPg-3D6wpWjSf2IBPwDn_9v7sxoz9M/s1600/Screen+Shot+2018-03-13+at+10.53.11+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;583&quot; data-original-width=&quot;1118&quot; height=&quot;166&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihaTbo_gU6qTPzwYYI-vesyKNirFU7K61H7fdsLJ1sphW1BOxe4iMOQ5ebhxQaOVjfd_UrAper1Xpmg7a3xR_Psm8hjM8aaoMlzzo6QX1rqkznS-YPg-3D6wpWjSf2IBPwDn_9v7sxoz9M/s320/Screen+Shot+2018-03-13+at+10.53.11+AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The received email need to be forwarded somewhere like to your application for processing but that&#39;s overkill. I need something simpler. Here come SNS, Simple Notification Service. So the email can be forwarded to an SNS Topic. From the SNS Topic you can create Subscription that will forward the email to your actual email, finally.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh6j4y3U5mzWDTeCJ4ERamlsjChhOvbIL8XDRR_zoPbFimoRWX0xsA8ypdE3JFskWeOBwjEr1RBwmadJoad1ClE1rrqjZu7DnHBmSlkZe0Wwxb1sb-IThABlHhRA27FFnPn_51WyKmqpQn/s1600/Screen+Shot+2018-03-13+at+10.57.07+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;357&quot; data-original-width=&quot;979&quot; height=&quot;116&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh6j4y3U5mzWDTeCJ4ERamlsjChhOvbIL8XDRR_zoPbFimoRWX0xsA8ypdE3JFskWeOBwjEr1RBwmadJoad1ClE1rrqjZu7DnHBmSlkZe0Wwxb1sb-IThABlHhRA27FFnPn_51WyKmqpQn/s320/Screen+Shot+2018-03-13+at+10.57.07+AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQBrx8U8HoZLxH9uBv-28Fo_m3aAIqOqkEv9fmChapFC_JYeMI-x9ggJ9vT_QQ4xOPXi5nYJzk_cl-r2gtyrqMNj6ryEuUQp554Q5Saey7XmDru4NhSGC-ygouPVc1fXo1ZMC1W72S16Cv/s1600/Screen+Shot+2018-03-13+at+10.57.56+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;512&quot; data-original-width=&quot;1279&quot; height=&quot;128&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQBrx8U8HoZLxH9uBv-28Fo_m3aAIqOqkEv9fmChapFC_JYeMI-x9ggJ9vT_QQ4xOPXi5nYJzk_cl-r2gtyrqMNj6ryEuUQp554Q5Saey7XmDru4NhSGC-ygouPVc1fXo1ZMC1W72S16Cv/s320/Screen+Shot+2018-03-13+at+10.57.56+AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
SNS will send the email as JSON so if you received HTML email that will be appear in your inbox as is. But since I can read HTML, that should be fine.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/869453395265260033/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/869453395265260033?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/869453395265260033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/869453395265260033'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2018/03/receiving-email-using-ses.html' title='Receiving Email Using SES'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihaTbo_gU6qTPzwYYI-vesyKNirFU7K61H7fdsLJ1sphW1BOxe4iMOQ5ebhxQaOVjfd_UrAper1Xpmg7a3xR_Psm8hjM8aaoMlzzo6QX1rqkznS-YPg-3D6wpWjSf2IBPwDn_9v7sxoz9M/s72-c/Screen+Shot+2018-03-13+at+10.53.11+AM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-2151126504109417354</id><published>2017-11-10T08:35:00.001+08:00</published><updated>2017-11-10T08:35:41.211+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="decentralized"/><category scheme="http://www.blogger.com/atom/ns#" term="p2p"/><title type='text'>The rise of localhost</title><content type='html'>I noticed a pattern in &lt;a href=&quot;https://www.scuttlebutt.nz/glossary.html#dex&quot; rel=&quot;nofollow&quot;&gt;dex&lt;/a&gt; world, where you build client backend to participate in the network, and then build a web app that simply connect to&amp;nbsp;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;localhost:someport&amp;nbsp;&lt;/span&gt;for the UI.&lt;br /&gt;
&lt;br /&gt;
To check my scuttlebutt updates, I opened up http://localhost:8027/. For those using Ethereum Parity wallet, they can open it at&amp;nbsp;http://localhost:8180/. ZeroNet users are browsing at&amp;nbsp;http://localhost:43110/.&lt;br /&gt;
&lt;br /&gt;
But Parity for example, try to make it seamless, they still provide a dns - web3.site which then redirected to home.web3.site which simply resolved to 127.0.0.1. But this I think bring up some problem, especially non-tech user which think that Parity is a website hosted by Parity Technologies. I seen this in a some articles about the latest &lt;a href=&quot;http://telegra.ph/Summary-of-recent-Parity-hack-11-09&quot;&gt;bug&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/2151126504109417354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/2151126504109417354?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2151126504109417354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2151126504109417354'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/11/the-rise-of-localhost_10.html' title='The rise of localhost'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-1410126110233880932</id><published>2017-10-11T07:19:00.001+08:00</published><updated>2017-10-11T08:20:21.066+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>The first step in learning new programming language</title><content type='html'>Is to prepare the basic environment where you can freely try and experiment with the new language features and tools. Maybe because I&#39;m not programmer type person, but more as tinkerer/builder, I hate learning the language syntax and stuff.&lt;br /&gt;
&lt;br /&gt;
For years after I started &quot;learning&quot; Python, I can&#39;t barely write any Python code. But I have manage to try lot of Python cool apps because I have that environment for me to experiment with all Python based applications. All these cool apps that get me hooked to the language, not the syntax or whatever language features. And in my experiences, this is one reason why people failed to get hooked on the new language they want to learn. They started learning with some of the language syntax and eventually get bored, because not so much interesting stuff there. In whatever programming language, it&#39;s the ecosystem that made it lively, and where the real work happened.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4rDlSF5q5u6jW5wjxrR2GmqhqiMWH1xf_PAmX5jQQ7h_ONVY5gOBdf2NlIFFWWfd2wsM3zNhHDI7CiY3ZM4GzzARVCVaOau_Mnmr7Cfg3-tDyZzM9Mu2lR8XhL3X_dzBVgvquhUgCRfC2/s1600/IMG_20170418_152401%25231.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1200&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4rDlSF5q5u6jW5wjxrR2GmqhqiMWH1xf_PAmX5jQQ7h_ONVY5gOBdf2NlIFFWWfd2wsM3zNhHDI7CiY3ZM4GzzARVCVaOau_Mnmr7Cfg3-tDyZzM9Mu2lR8XhL3X_dzBVgvquhUgCRfC2/s320/IMG_20170418_152401%25231.jpg&quot; width=&quot;240&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Early this year, I made it a point to &lt;a href=&quot;http://k4ml.me/learn-go/&quot;&gt;learn Go&lt;/a&gt; programming language. It&#39;s almost a year now and similar to my Python experience I mentioned above, I can&#39;t barely write any Go code. But I have &lt;a href=&quot;https://github.com/devkini/notes/wiki/Go&quot;&gt;an environment&lt;/a&gt; where I can try a lot of Go based applications, and this is the reason I&#39;m still hooked to the language even I haven&#39;t manage to write my own code yet. Back with Python experience, I&#39;d only manage to know Python and write Python code after I got my first Python job. That was years after I started learning the language.&lt;br /&gt;
&lt;br /&gt;
The reason this work I think similar to when learning human language. To master the language, you need to practice it. And to practice the language, you need to be in an environment where the language being used. That&#39;s it, the environment. So instead of trying to understand the for loop or the class syntax, I spent time to understand how packaging and deployment work, so I can actually run the application for other people to make use of it.&lt;br /&gt;
&lt;br /&gt;
So start to invest your time in having the environment first, whenever you want to learn any new programming language.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/1410126110233880932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/1410126110233880932?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1410126110233880932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1410126110233880932'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/10/the-first-step-in-learning-new.html' title='The first step in learning new programming language'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4rDlSF5q5u6jW5wjxrR2GmqhqiMWH1xf_PAmX5jQQ7h_ONVY5gOBdf2NlIFFWWfd2wsM3zNhHDI7CiY3ZM4GzzARVCVaOau_Mnmr7Cfg3-tDyZzM9Mu2lR8XhL3X_dzBVgvquhUgCRfC2/s72-c/IMG_20170418_152401%25231.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-2214697281792843829</id><published>2017-09-14T09:29:00.000+08:00</published><updated>2017-09-14T09:29:58.699+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="aws"/><category scheme="http://www.blogger.com/atom/ns#" term="lightsail"/><category scheme="http://www.blogger.com/atom/ns#" term="vps"/><title type='text'>Notes on AWS Lightsail</title><content type='html'>Finally, I&#39;m trying Amazon Lightsail. Just few notes:-&lt;span style=&quot;background-color: white; color: rgba(0 , 0 , 0 , 0.7); font-family: &amp;quot;source sans pro&amp;quot; , &amp;quot;helvetica&amp;quot; , &amp;quot;arial&amp;quot; , sans-serif , &amp;quot;hiragino kaku gothic pro&amp;quot; , &amp;quot;meiryo&amp;quot; , &amp;quot;hiragino sans gb w3&amp;quot; , &amp;quot;noto naskh arabic&amp;quot; , &amp;quot;droid arabic naskh&amp;quot; , &amp;quot;geeza pro&amp;quot; , &amp;quot;simplified arabic&amp;quot; , &amp;quot;noto sans thai&amp;quot; , &amp;quot;thonburi&amp;quot; , &amp;quot;dokchampa&amp;quot; , &amp;quot;droid sans thai&amp;quot; , &amp;quot;droid sans fallback&amp;quot; , , &amp;quot;.sfnsdisplay-regular&amp;quot; , &amp;quot;heiti sc&amp;quot; , &amp;quot;microsoft yahei&amp;quot; , &amp;quot;segoe ui&amp;quot;; font-size: 15px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFlrl6PQSrMzKZwF4n38Eme092dvUFzbnSGSFB6ArY1dvd_-qqB5kUfhFyIGEbE-9xAWceu1JYBgJDuHh_B8__ZuoqbrwZR9MsdtHCAT6dO0mguMCi49XcEfd9nO-Af9Tc3ZQaiQKFosqx/s1600/Screen+Shot+2017-09-14+at+9.27.09+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;517&quot; data-original-width=&quot;1195&quot; height=&quot;138&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFlrl6PQSrMzKZwF4n38Eme092dvUFzbnSGSFB6ArY1dvd_-qqB5kUfhFyIGEbE-9xAWceu1JYBgJDuHh_B8__ZuoqbrwZR9MsdtHCAT6dO0mguMCi49XcEfd9nO-Af9Tc3ZQaiQKFosqx/s320/Screen+Shot+2017-09-14+at+9.27.09+AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;1. The direct console is great. This is basically what EC2 should have, instead of web ssh console. You need this console to verify the ssh key fingerprint before connecting through ssh first time.&lt;div&gt;
2. There&#39;s simple firewall manager. Only port 22 and 80 opened by default. Click Manage from the instance menu to get into the firewall manager.&lt;/div&gt;
&lt;div&gt;
3. Now there&#39;s apac region !&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/2214697281792843829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/2214697281792843829?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2214697281792843829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2214697281792843829'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/09/notes-on-aws-lightsail.html' title='Notes on AWS Lightsail'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFlrl6PQSrMzKZwF4n38Eme092dvUFzbnSGSFB6ArY1dvd_-qqB5kUfhFyIGEbE-9xAWceu1JYBgJDuHh_B8__ZuoqbrwZR9MsdtHCAT6dO0mguMCi49XcEfd9nO-Af9Tc3ZQaiQKFosqx/s72-c/Screen+Shot+2017-09-14+at+9.27.09+AM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-677355585044177303</id><published>2017-09-08T08:05:00.000+08:00</published><updated>2017-09-08T08:05:06.741+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="nginx"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="unit"/><title type='text'>A brief look into nginx unit</title><content type='html'>So nginx has come out with it&#39;s own application server that can run multiple types of application from PHP, Python and Go, with more languages support coming soon. Deployment is always my &lt;a href=&quot;http://web.archive.org/web/20100103094942/http://www.k4ml.com:80/taxonomy/term/25&quot; rel=&quot;nofollow&quot;&gt;topic&lt;/a&gt; of &lt;a href=&quot;http://web.archive.org/web/20100116051622/http://www.k4ml.com:80/node/269&quot; rel=&quot;nofollow&quot;&gt;interest&lt;/a&gt;&amp;nbsp;so this kind of news definitely caught my eyes.&lt;br /&gt;
&lt;br /&gt;
There&#39;s not much technical information yet, other than the &lt;a href=&quot;https://www.nginx.com/blog/introducing-nginx-application-platform/&quot; rel=&quot;nofollow&quot;&gt;official blog post&lt;/a&gt;. I&#39;m quite interested to know how the python support being implemented. The &lt;a href=&quot;http://unit.nginx.org/docs-configuration.html#applications&quot; rel=&quot;nofollow&quot;&gt;Application model&lt;/a&gt; also remind me to the &lt;a href=&quot;https://docs.webfaction.com/xmlrpc-api/apiref.html#applications&quot; rel=&quot;nofollow&quot;&gt;Webfaction hosting model&lt;/a&gt; and with unit HTTP API, it provide possibility to implement something like Webfaction hosting panel where you can add new application and manage it all through just a web interface.&lt;br /&gt;
&lt;br /&gt;
Reading through the &lt;a href=&quot;https://github.com/nginx/unit/blob/master/src/nxt_python_wsgi.c&quot; rel=&quot;nofollow&quot;&gt;source code of python module&lt;/a&gt; support, it look like python wsgi application is being executed in-process through python C API (my guess), particularly if you look at the line 337:-&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;background-color: #fffbdd; color: #24292e; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;result = &lt;/span&gt;&lt;span class=&quot;pl-c1&quot; style=&quot;box-sizing: border-box; color: #005cc5; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;PyObject_CallObject&lt;/span&gt;&lt;span style=&quot;background-color: #fffbdd; color: #24292e; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;(nxt_py_application, args);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: #fffbdd; color: #24292e; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
For &lt;a href=&quot;https://github.com/nginx/unit/blob/master/src/nxt_go.c&quot; rel=&quot;nofollow&quot;&gt;Go application&lt;/a&gt; however it simply execute the Go built-in http server and communicate through that. The current implementation is merely 100 lines compared to python implementation which is almost 1000 lines.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Enough with all the theories, I decided to give it a try. It has deb packages for Ubuntu Xenial. Just follow the documentation how to install it through apt. The docs however seem missing on how to start unit after installing it. After scouring around the apt file then I know the binary is named as unitd, so to start it:-&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;sudo systemctl start unitd&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
After that you can start communicating with unitd through the HTTP JSON API:-&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;sudo curl --unix-socket /var/run/control.unit.sock http://localhost/&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Btw, it also through this I learned that curl has unix socket support !&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
For the rest, you can just follow the docs. I did try with simple wsgi applications and discovered few issues:-&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;https://github.com/nginx/unit/issues/17 - How to apply changes to our app script ?&lt;/li&gt;
&lt;li&gt;https://github.com/nginx/unit/issues/16 - All applications and listeners lost after restarting unitd&lt;/li&gt;
&lt;li&gt;https://github.com/nginx/unit/issues/12 - How to inject environment variables ?&lt;/li&gt;
&lt;li&gt;https://github.com/nginx/unit/issues/15 - How to add venv packages ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
That&#39;s all for now. Next probably trying it from source instead of packages to get all the latest changes.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/677355585044177303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/677355585044177303?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/677355585044177303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/677355585044177303'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/09/a-brief-look-into-nginx-unit.html' title='A brief look into nginx unit'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-2435888073730636414</id><published>2017-08-15T08:41:00.002+08:00</published><updated>2017-08-15T08:41:47.198+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dns"/><category scheme="http://www.blogger.com/atom/ns#" term="open-source"/><title type='text'>List of DNS server software</title><content type='html'>It&#39;s been quite a long time I&#39;m not following the recent development in dns scene, especially on the server software itself. In the past, I did try setting up my own BIND installation just to understand how all these things work.&lt;br /&gt;
&lt;br /&gt;
I tweet reminded me back:-&lt;br /&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;
&lt;div dir=&quot;ltr&quot; lang=&quot;in&quot;&gt;
Best &lt;a href=&quot;https://twitter.com/hashtag/PowerDNS?src=hash&quot;&gt;#PowerDNS&lt;/a&gt; ni, siap http api.  Auto SOA increment. &lt;a href=&quot;https://twitter.com/hashtag/thorbaek?src=hash&quot;&gt;#thorbaek&lt;/a&gt;&lt;/div&gt;
— FryShadow (@FryShadow) &lt;a href=&quot;https://twitter.com/FryShadow/status/897248814892851200&quot;&gt;August 15, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; charset=&quot;utf-8&quot; src=&quot;//platform.twitter.com/widgets.js&quot;&gt;&lt;/script&gt;

So what else dns server software we have these days other than BIND 9 ?&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.powerdns.com/opensource.html&quot; rel=&quot;nofollow&quot;&gt;PowerDNS&lt;/a&gt; (as in tweet above).&lt;/li&gt;
&lt;li&gt;CoreDNS - now also a server type plugin of Caddy. It&#39;s a successor of SkyDNS.&lt;/li&gt;
&lt;li&gt;SkyDNS -&amp;nbsp;SkyDNS is a distributed service for announcement and discovery of services built on top of &lt;a href=&quot;https://github.com/coreos/etcd&quot;&gt;etcd&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Knot&lt;/li&gt;
&lt;li&gt;NSD&lt;/li&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/2435888073730636414/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/2435888073730636414?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2435888073730636414'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2435888073730636414'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/08/list-of-dns-server-software.html' title='List of DNS server software'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-8597571743734508639</id><published>2017-07-24T09:03:00.003+08:00</published><updated>2017-12-17T09:22:19.882+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bitcoin"/><category scheme="http://www.blogger.com/atom/ns#" term="blockchain"/><category scheme="http://www.blogger.com/atom/ns#" term="smart-contract"/><title type='text'>How smart-contract is possible in blockchain ?</title><content type='html'>The idea was derived from the scripting support in bitcoin. Part of bitcoin transaction contain a Script, a stack based language that will be executed to verify the transaction. Complex conditions can be expressed in this language, which has 80 different opcodes including arithmetic, bitwise operations, string operations, conditionals, and stack manipulation.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
This language however still pretty limited, such as not having loop and thus not Turing complete. So new blockchain implementation like Etherium expand the idea further to include support for real programming language, thus enabling more complex conditions to the blockchain transaction, which now commonly known as &lt;a href=&quot;https://www.reddit.com/r/programming/comments/7fuvfe/understanding_ethereum_smart_contracts/&quot; rel=&quot;nofollow&quot;&gt;smart-contract&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Side note, making &lt;a href=&quot;http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html&quot; rel=&quot;nofollow&quot;&gt;bitcoin transaction in python&lt;/a&gt;.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/8597571743734508639/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/8597571743734508639?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8597571743734508639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8597571743734508639'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/07/how-smart-contract-is-possible-in.html' title='How smart-contract is possible in blockchain ?'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-1139142415000674133</id><published>2017-06-11T09:36:00.004+08:00</published><updated>2021-04-13T09:11:20.966+08:00</updated><title type='text'>How to build your own programming language</title><content type='html'>I&#39;ve always fascinated in how programming languages are built and have made it into my 2017&#39;s new year resolution to learn building one myself. So here are all the notes collected along the way.&lt;br /&gt;
&lt;br /&gt;
My presentation at Malaysia Open Source Conference - &lt;a href=&quot;https://docs.google.com/presentation/d/1sZ6GwUQ3NabokEbQyGU-9jJG3_jCdftnWa4brwvTCa0/edit?usp=sharing&quot;&gt;How to build your programming language ?&lt;/a&gt;&amp;nbsp;I plan to extend this presentation at upcoming &lt;a href=&quot;https://pycon.my/&quot; rel=&quot;nofollow&quot;&gt;PyCon APAC&lt;/a&gt;&amp;nbsp;in this August with an example in Python.&lt;br /&gt;
&lt;br /&gt;
A write up in Malay - &lt;a href=&quot;https://koditi.my/bagaimana-bahasa-pengaturcaraan/&quot;&gt;Bagaimana bahasa pengaturcaraan dibina&lt;/a&gt; ?&lt;br /&gt;
&lt;br /&gt;
A series on building simple interpreter in Python by Ruslan Plivak - &lt;a href=&quot;https://ruslanspivak.com/lsbasi-part1/&quot;&gt;Let&#39;s Build a Simple Interpreter&lt;/a&gt; !&lt;br /&gt;
&lt;br /&gt;
A book on writing an interpreter with Go (Golang). I&#39;m still making up my mind whether to buy this or not -&amp;nbsp;&lt;a href=&quot;https://interpreterbook.com/&quot;&gt;https://interpreterbook.com/&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.reddit.com/r/programming/comments/5o61lo/the_first_few_chapters_of_my_free_book_on/&quot;&gt;Free book on building interpreter using Java and then C&lt;/a&gt;. This is not complete yet but the author also recommend Interpreter book above in his reply to my reddit&#39;s comment. (Updates 2020: It&#39;s &lt;a href=&quot;https://mobile.twitter.com/k4ml/status/1248428444288835587&quot;&gt;complete&lt;/a&gt; now!)&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;List of &lt;a href=&quot;https://www.reddit.com/domain/craftinginterpreters.com/&quot; rel=&quot;nofollow&quot;&gt;reddit&#39;s threads&lt;/a&gt; related to chapters in this book.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
&lt;br /&gt;
&lt;br /&gt;
A classic on building compiler, the original essay was using Pascal. This is my first time trying Pascal and to my surprise the example code (from 1986) still compile in the modern FreePascal - &lt;a href=&quot;http://compilers.iecc.com/crenshaw/&quot; rel=&quot;nofollow&quot;&gt;Let&#39;s Build a Compiler&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
A &lt;a href=&quot;https://www.reddit.com/r/learnprogramming/comments/2p6h3e/how_can_i_learn_to_write_my_own_programming/&quot; rel=&quot;nofollow&quot;&gt;question on reddit&lt;/a&gt; on how to build own programming language.&lt;br /&gt;
&lt;br /&gt;
Creating &lt;a href=&quot;https://ralsina.me/weblog/posts/creating-languages-for-dummies.html#intro&quot; rel=&quot;nofollow&quot;&gt;toy programming language using Python PyParsing&lt;/a&gt; library.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://nicoleorchard.com/blog/compilers&quot; rel=&quot;nofollow&quot;&gt;An Intro to Compilers&lt;/a&gt;&amp;nbsp;- A step by step guide on how a simple &quot;Hello World&quot; in C get pre-processed, tokenized, parsed, optimized and finally compiled to machine language using LLVM frontend and backend tools. Very interesting read and easy to understand.&lt;br /&gt;
&lt;br /&gt;
Using &lt;a href=&quot;https://medium.com/@marcelogdeandrade/writing-your-own-programming-language-and-compiler-with-python-a468970ae6df&quot; rel=&quot;nofollow&quot;&gt;Python ply and llvmlite&lt;/a&gt; to build programming language.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;a href=&quot;https://tenthousandmeters.com/blog/python-behind-the-scenes-1-how-the-cpython-vm-works/&quot;&gt;Python behind the scenes #1: how the CPython VM works&lt;br /&gt;&lt;/a&gt;&lt;p&gt;
&lt;br /&gt;
Programming language zoo -&amp;nbsp;&lt;a href=&quot;https://plzoo.andrej.com/&quot;&gt;https://plzoo.andrej.com/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
https://github.com/jamiebuilds/the-super-tiny-compiler/blob/master/the-super-tiny-compiler.js&lt;br /&gt;
&lt;br /&gt;
https://medium.com/dailyjs/compiler-in-javascript-using-antlr-9ec53fd2780f&amp;nbsp;&lt;/p&gt;&lt;p&gt;https://speakerdeck.com/pyconslides/so-you-want-to-write-an-interpreter-by-alex-gaynor?slide=43 &lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/1139142415000674133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/1139142415000674133?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1139142415000674133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1139142415000674133'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2017/06/how-to-build-your-own-programming.html' title='How to build your own programming language'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-6226752158583196728</id><published>2016-12-23T08:33:00.003+08:00</published><updated>2016-12-23T08:33:55.763+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="buildout"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>My Python Workflow</title><content type='html'>This is basically what I did whenever starting on new Python project.&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;white-space: pre-wrap; word-wrap: break-word;&quot;&gt;mkdir project_name
cd project_name
wget https://bootstrap.pypa.io/bootstrap-buildout.py
vim buildout.cfg
&lt;/pre&gt;
&lt;br /&gt;
Where my buildout.cfg look like:-&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;white-space: pre-wrap; word-wrap: break-word;&quot;&gt;[buildout]
parts = main

[main]
recipe = zc.recipe.egg
eggs =
    python-telegram-bot

interpreter = python&lt;/pre&gt;
&lt;br /&gt;
&lt;pre style=&quot;white-space: pre-wrap; word-wrap: break-word;&quot;&gt;&lt;/pre&gt;
Then I just execute the following to get my new python environment initialize:-&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre style=&quot;white-space: pre-wrap; word-wrap: break-word;&quot;&gt;python bootstrap.py
./bin/buildout
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
A python interpreter that already has access to my packages dependencies - in this case python-telegram-bot is ready in &lt;code&gt;./bin/python&lt;/code&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;m a fan of buildout and shall write more about it in later post.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/6226752158583196728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/6226752158583196728?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/6226752158583196728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/6226752158583196728'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2016/12/my-python-workflow.html' title='My Python Workflow'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-5583539310136823129</id><published>2015-09-11T06:35:00.000+08:00</published><updated>2015-09-11T06:43:35.555+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ajenti"/><category scheme="http://www.blogger.com/atom/ns#" term="django"/><category scheme="http://www.blogger.com/atom/ns#" term="hosting"/><category scheme="http://www.blogger.com/atom/ns#" term="nginx"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="vps"/><title type='text'>Trying out Ajenti</title><content type='html'>Ajenti is server control panel, in the same space as Webmin. It allow you to manage your linux server directy via web interface.&lt;br /&gt;
&lt;br /&gt;
In term of installation, Ajenti really win. All available as OS packages, so if using Ubuntu or Debian it just a matter of apt-get. The initial installation is covered by a script that you download from Ajenti&#39;s website.&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;background-color: whitesmoke; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 4px; border-top-right-radius: 4px; border: 1px solid rgb(216, 216, 216); box-sizing: border-box; color: #262626; font-family: Menlo, Monaco, Consolas, &#39;Courier New&#39;, monospace; font-size: 13px; line-height: 18.5714302062988px; margin-bottom: 10px; overflow: auto; padding: 9.5px; word-break: break-all; word-wrap: break-word;&quot;&gt;wget -O- https://raw.github.com/ajenti/ajenti/1.x/scripts/install-ubuntu.sh | sudo sh&lt;/pre&gt;
One thing that tripped me up is that I thought the web hosting plugins also in the same packages. Turn out it a &lt;a href=&quot;https://twitter.com/ajenti/status/450947035420950528&quot; rel=&quot;nofollow&quot;&gt;separate package called ajenti-v&lt;/a&gt; (It clearly shown on the website, so my bad). To get the webhosting packages you need to install ajenti-v package.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre&gt;sudo apt-get install ajenti-v ajenti-v-nginx ajenti-v-mysql ajenti-v-php-fpm php5-mysql ajenti-v-python-gunicorn&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
If you have already install apache2 before, you need to remove it as Ajenti-v use nginx.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The whole experiences of getting a Django website running is still not very smooth. Those who are not familiar with Python deployment might be in hard time trying to fit everything together. This is I some room that I want to explore me in Ajenti-V. In order for this to be really usable, we don&#39;t have to touch the terminal at all to get things running.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The first problem I got is&amp;nbsp;&lt;/div&gt;
supervisor FATAL Exited too quickly (process log may have details). This turn out that I don&#39;t have gunicorn installed in the virtualenv set for my site. There are lot of things that need to be set - virtualenv, install gunicorn, the path to wsgi script.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
One that taking so much of my time is figuring out why nginx keep doing 301 redirect for static files. In the end, I have to choose the &#39;root&#39; method instead of &#39;alias&#39; in nginx location.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
http://nginx.2469901.n2.nabble.com/stop-automatic-trailing-slash-addition-td7592389.html&lt;br /&gt;
http://wiki.nginx.org/HttpCoreModule#alias&lt;br /&gt;
&lt;br /&gt;
There&#39;s a subtle difference between root and alias.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/5583539310136823129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/5583539310136823129?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/5583539310136823129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/5583539310136823129'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2015/09/trying-out-ajenti.html' title='Trying out Ajenti'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-8077300595081236836</id><published>2014-08-21T16:44:00.001+08:00</published><updated>2014-08-21T16:44:11.676+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="aws"/><category scheme="http://www.blogger.com/atom/ns#" term="ec2"/><title type='text'>AWS: Allowing IAM user to manage their own MFA device</title><content type='html'>When enabling MFA (Multi-factor Authentication) on AWS Web Console, only users with admin privilege can configure the MFA device for each IAM user. This pose a problem if your users are not in the same physical location. To allow each IAM user to be able to configure the device on their own, you must add specific IAM policy:-&lt;br /&gt;
&lt;br /&gt;
http://docs.aws.amazon.com/IAM/latest/UserGuide/Credentials-Permissions-examples.html#creds-policies-mfa-console&lt;br /&gt;
&lt;br /&gt;
If you&#39;re using the default&amp;nbsp;PowerUserAccess, that policy also basically remove access to the whole IAM so make sure to change that too. The default policy:-&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp; &quot;Version&quot;: &quot;2012-10-17&quot;,&lt;br /&gt;
&amp;nbsp; &quot;Statement&quot;: [&lt;br /&gt;
&amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;Effect&quot;: &quot;Allow&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;NotAction&quot;: &quot;iam:*&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;Resource&quot;: &quot;*&quot;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; ]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Change that to:-&lt;br /&gt;
&lt;pre style=&quot;background-color: white; border: none; box-sizing: border-box; color: #444444; font-family: Consolas, &#39;Bitstream Vera Sans Mono&#39;, Monaco, monospace; font-size: 14px; line-height: 18.200000762939453px; margin-bottom: 5px; margin-top: 5px; overflow: auto; padding: 10px;&quot;&gt;{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: &quot;ec2:*&quot;,
      &quot;Resource&quot;: &quot;arn:aws:ec2:*&quot;
    }
  ]
}&lt;/pre&gt;
Finally, user also has at least read only access to the IAM.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/8077300595081236836/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/8077300595081236836?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8077300595081236836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8077300595081236836'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2014/08/aws-allowing-iam-user-to-manage-their.html' title='AWS: Allowing IAM user to manage their own MFA device'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-7168608686737410841</id><published>2013-06-13T13:37:00.003+08:00</published><updated>2013-06-13T13:37:49.641+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="apache"/><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><title type='text'>Apache read raw request data</title><content type='html'>It&#39;s not possible to get the whole raw request data from within PHP. You can get the request body using php://input but not the headers. Searching around, I found that you can log the request data from apache &amp;nbsp;using &lt;a href=&quot;https://httpd.apache.org/docs/2.4/mod/mod_dumpio.html&quot; rel=&quot;nofollow&quot;&gt;mod_dumpio&lt;/a&gt;. It will dump the incoming request data to error.log. As mentioned in the docs, for apache version &amp;lt; 2.4, you have to set LogLevel to debug. One catch with this is to make sure none of your virtualhost config has LogLevel higher than debug otherwise you&#39;ll not get output from this module. Also make sure you didn&#39;t set the LogLevel to debug but going down the config, another LogLevel exists and set to something else. Happened to me.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/7168608686737410841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/7168608686737410841?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/7168608686737410841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/7168608686737410841'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2013/06/apache-read-raw-request-data.html' title='Apache read raw request data'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-2497903733829502890</id><published>2013-05-27T03:48:00.001+08:00</published><updated>2013-05-27T03:51:01.697+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="nc"/><category scheme="http://www.blogger.com/atom/ns#" term="telnet"/><title type='text'>Craft HTTP requests using nc</title><content type='html'>&lt;p&gt;To do some low level check on websites, I&#39;d usually use telnet to compose a http requests against the server. The main intention is to talk directly to the server port to make sure the problem we have not caused by some higher level application. For example, to connect to server and issue a GET request:-&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;telnet k4ml.github.io 80
Trying 199.27.75.133...
Connected to github.map.fastly.net.
Escape character is &#39;^]&#39;.
GET /
Connection closed by foreign host.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&#39;s always a problem with telnet. In the above example, I can only issue a GET request without having a chance to add other HTTP headers such as HOST before the server close the connection. Some websites also time out very quickly when they are not receiving any data after establishing connection. And since the above command is in interactive session, it&#39;s not repeatable or scripted. Using &lt;code&gt;nc&lt;/code&gt; seem to be much better.&lt;/p&gt;

&lt;p&gt;Specify the virtual host:-&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;echo -en &quot;HEAD / HTTP/1.1\r\nHOST: k4ml.github.io\r\n\r\n&quot; | nc k4ml.github.io 80
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&#39;ll get the output as:-&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
Server: GitHub.com
Content-Type: text/html
Last-Modified: Fri, 12 Apr 2013 23:26:51 GMT
Expires: Sun, 26 May 2013 19:40:06 GMT
Cache-Control: max-age=600
Content-Length: 9991
Accept-Ranges: bytes
Date: Sun, 26 May 2013 19:30:07 GMT
Via: 1.1 varnish
Age: 0
Connection: keep-alive
X-Served-By: cache-s34-SJC2
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1369596606.987305880,VS0,VE145
Vary: Accept-Encoding
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Without virtualhost:-&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;echo -en &quot;HEAD / HTTP/1.1\r\n\r\n&quot; | nc k4ml.github.io 80
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And the output:-&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;HTTP/1.1 400 Bad Request
Server: GitHub.com
Content-Type: text/html
Content-Length: 166
Accept-Ranges: bytes
Date: Sun, 26 May 2013 19:32:16 GMT
Via: 1.1 varnish
Age: 0
Connection: keep-alive
X-Served-By: cache-s35-SJC2
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1369596736.277374744,VS0,VE72
Vary: Accept-Encoding
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It allow us to fully compose the request and then send it through the opened connection nc created.&lt;/p&gt;

&lt;p&gt;Reference:-&lt;/p&gt;
http://www.philandstuff.com/2013/05/17/statsd-netcat.html</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/2497903733829502890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/2497903733829502890?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2497903733829502890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/2497903733829502890'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2013/05/craft-http-requests-using-nc.html' title='Craft HTTP requests using nc'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-5660069993549129543</id><published>2012-04-18T05:51:00.000+08:00</published><updated>2012-04-18T05:51:07.789+08:00</updated><title type='text'>Weekend project</title><content type='html'>Quick weekend project - a website to search for &#39;halal&#39; status of products in local market. The data was scraped from JAKIM website. The primary motivation for doing this is to search for halal status from my mobile phone -     small feature phone with browser (not android). The JAKIM website can&#39;t even being displayed on my phone.

It used Django at the backend and the well known Twitter Bootstrap for the frontend page. This is my first use of Bootstrap, it simply work out of the box for mobile browser (mine is the old opera mini, 3.0 something I guess). I try to avoid Django for weekend project but the offer django-haystack has for quick search tool is too tempting so I decided to still use Django for this one. The search backend is Whoosh with the integration mostly done by haystack.

Scraping JAKIM website not easy, the data are all in heavily nested html tables with no id or class to identify. I use python lxml lib to parse the html. When user search for keyword, it will look first in the Whoosh index and if none found, try to query JAKIM site directly and then redirect user to the same page with their query parameter. To make the result immediately available, I used the real time index feature of haystack that will automatically update the index once new item inserted into db.

This site has one advantage over the JAKIM site. The search on JAKIM site was naively implemented that you can&#39;t even search for multiple keywords. For example searching for &quot;shokubutsu original&quot; yield no result from JAKIM while my site return exactly what I want.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/5660069993549129543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/5660069993549129543?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/5660069993549129543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/5660069993549129543'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2012/04/weekend-project.html' title='Weekend project'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-1785286917918765696</id><published>2012-04-15T12:55:00.000+08:00</published><updated>2012-04-15T12:56:20.666+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="lxml"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="unicode"/><title type='text'>Django lxml encode error</title><content type='html'>Python script using lxml library that work fine on console suddenly throwing out error when importing it from django views module.

&lt;pre&gt;
File &quot;lxml.etree.pyx&quot;, line 123, in init lxml.etree (src/lxml/lxml.etree.c:160385) TypeError: encode() argument 1 must be string without null bytes, not unicode&quot;
&lt;/pre&gt;

It unlikely problem with the encoding of the content I want to parse because it just importing the module and I&#39;m not calling any function that do the parsing yet. Almost giving up in my search until I found this answer[1] on Stackoverflow. It turn out on console I&#39;m using Python 2.6 while mod_wsgi, which run the django app is compiled against python 2.7.

&lt;br /&gt;
[1]:http://stackoverflow.com/questions/9465248/runtimewarning-compiletime-version-2-6-of-module-lxml-etree-does-not-match-ru</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/1785286917918765696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/1785286917918765696?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1785286917918765696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/1785286917918765696'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2012/04/django-lxml-encode-error.html' title='Django lxml encode error'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-7767454427293905152</id><published>2012-02-11T08:43:00.000+08:00</published><updated>2012-10-31T18:06:32.418+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="postgresql"/><title type='text'>PostgreSQL dump list file</title><content type='html'>The error first started with something like &#39;INVALID COMMAND \N ..&#39;. This actually not the real error. Use psql command line switch --set ON_ERROR_STOP=1 to stop immediately on error so we can see the actual error.&lt;br /&gt;&lt;br /&gt;
 
When restoring my postgresql dump to the new hosting, got error that saying plpgsql language already exists. This probably because my hosting already setup plpgsql in all databases by default while dump consist a line to create the language extension. Googling around, I found out that pg_restore provide what they call list file that list out what kind of object should be restored. So the first step is to generate the list file out of your database dump:-

&lt;br /&gt;
&lt;pre&gt;pg_restore -l db.dump &amp;gt; db.list
&lt;/pre&gt;
db.dump is your database dump file and when specifying -l option, the output would be list of object to restore. We save that list in db.list file. Now we can open up db.list with any text editor and comment out the line that mention the creation of plpgsql. The list file should look something like:-

&lt;br /&gt;
&lt;pre&gt;3178; 1262 1525521 DATABASE - cc_live myname
6; 2615 1313721 SCHEMA - audit myname
7; 2615 1313722 SCHEMA - cct myname
3; 2615 2200 SCHEMA - public postgres
3179; 0 0 COMMENT - SCHEMA public postgres
3180; 0 0 ACL - public postgres
;1007; 2612 1313725 PROCEDURAL LANGUAGE - plpgsql myname
639; 1247 1313728 TYPE cct daily_sale myname
641; 1247 1313729 DOMAIN public bigint_unsigned myname
&lt;/pre&gt;
So we comment out (by putting semicolon at the beginning of the line) plpgsql object. Then we restoring the dump, we specify the list file to pg_restore:-

&lt;br /&gt;
&lt;pre&gt;pg_restore -x -O -L db.list db.dump | psql new_db 
&lt;/pre&gt;
This is useful if the dump file very large or using the archive format which mean you can&#39;t edit it directly with editor. The list file supposed to be editable.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/7767454427293905152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/7767454427293905152?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/7767454427293905152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/7767454427293905152'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2012/02/postgresql-dump-list-file.html' title='PostgreSQL dump list file'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-6596394585125404459</id><published>2012-01-12T18:49:00.000+08:00</published><updated>2012-01-12T18:49:01.995+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qmail"/><category scheme="http://www.blogger.com/atom/ns#" term="smtp"/><category scheme="http://www.blogger.com/atom/ns#" term="telnet"/><title type='text'>Test sending email through telnet</title><content type='html'>This the minimal telnet session to test sending to email server.&lt;br /&gt;
&lt;pre&gt;$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is &#39;^]&#39;.
220 example.com ESMTP
helo
250 example.com
mail from:me@gmail.com
250 ok
rcpt to:x9tayay@example.com
250 ok
data
354 go ahead
Subject: OTA

ota ajaj.
.
250 ok 1326364891 qp 1790
quit
221 example.com
&lt;/pre&gt;The mail server in this case running qmail. If the email is bounced, the returned email may end up in your spam folder as the sender domain does not match, we send this from localhost instead of from gmail.com.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/6596394585125404459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/6596394585125404459?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/6596394585125404459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/6596394585125404459'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2012/01/test-sending-email-through-telnet.html' title='Test sending email through telnet'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-8282470836054032056</id><published>2011-11-23T03:44:00.001+08:00</published><updated>2011-11-23T05:01:45.429+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="irc"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="time"/><title type='text'>Using IRC as Time Tracker</title><content type='html'>I have tried all sort of time tracker, todo lists and what not but still don&#39;t really get &amp;nbsp;what I want out of that tools. The requirement is very basic and simple, a tool that allow me to track how much time I have spent on certain things throughout the day. While there are lot of great tools with a degree of simplicity, it still having too much friction for me to effectively use them.&lt;br /&gt;
&lt;br /&gt;
Emacs orgmode &amp;nbsp;I think pretty much close to what I want. That just a guess since I just gleaned over it&#39;s features and what people are saying about it. But to learn emacs just to use this is a bit too much. I took note in vim in outline mode - that&#39;s how my mental mode work. It would be nice if vim can record the time whenever I add new outline. Most of the todo lists or time tracker out there only allow you to define simple title and explanation of the task. If that was web based app, don&#39;t ever think of having some outlining support.&lt;br /&gt;
&lt;br /&gt;
Then I noticed that it quite easy for me to rebuild my mental state on what I have done throughout the day by looking at my svn commit log or the skype chat log with my colleagues. So I stop looking &amp;nbsp;for the tools and just used the history browser of our svn to compose my daily report. It work well except that not all tasks would have a commit log - or in other word something that has svn repository. I might be doing some server config &amp;nbsp;or work in some web interface and all these not related to any of our svn repo.&lt;br /&gt;
&lt;br /&gt;
The skype chat log gave me an idea that I maybe can use it to track my time. Each post got a time stamp so it perfect. But to whom should I chat with ? Setup new fake account ? Possible but I think there must be a better way. Enter IRC. I used irssi as my irc client and since it console based app, that mean almost no context switch in order to use it. It just few SHIFT+Arrow key away from the current console. To use public irc server for this&amp;nbsp;definitely no-no. Setting up the whole irc server seem a bit over kill although is much easier nowadays thanks to apt-get.&lt;br /&gt;
&lt;br /&gt;
Searching around, I found &lt;a href=&quot;http://www.gohttp//www.electricmonk.nl/log/2009/09/14/hircd-minimal-irc-server-in-python/&quot;&gt;hircd&lt;/a&gt; - python code (in 400 lines) implementing basic IRC server. Simply run the python code and fire up irssi to connect to localhost. It just work. Automate some tasks such as automatically joining a channel, autolog on and I&#39;m ready to go. If you work on multiple project and want to split the log, we can just create new channel but for now I stay in single channel as it easy to keep track.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX73pQcwO3eoAZiTc8ygX-AcK5Fk-nR9eo3f5w50koSYR23tl_aENJU3yasX4ZEw5BlzLgfhxcyhUVuqAE5hxku3LAhoANnOLlRcPpyV14gCSRBXTM7zcvtrn3fVeYYeLimhRTfVS_kaEF/s1600/irssi.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;242&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX73pQcwO3eoAZiTc8ygX-AcK5Fk-nR9eo3f5w50koSYR23tl_aENJU3yasX4ZEw5BlzLgfhxcyhUVuqAE5hxku3LAhoANnOLlRcPpyV14gCSRBXTM7zcvtrn3fVeYYeLimhRTfVS_kaEF/s400/irssi.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
Todo:-&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
1. Put the chat log ($HOME/irclogs) into spideroak synced directory.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
2. Integrate with &lt;a href=&quot;http://www.pomodorotechnique.com/&quot;&gt;pormodoro&amp;nbsp;technique&lt;/a&gt; ?&amp;nbsp;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/8282470836054032056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/8282470836054032056?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8282470836054032056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/8282470836054032056'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2011/11/using-irc-as-time-tracker.html' title='Using IRC as Time Tracker'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX73pQcwO3eoAZiTc8ygX-AcK5Fk-nR9eo3f5w50koSYR23tl_aENJU3yasX4ZEw5BlzLgfhxcyhUVuqAE5hxku3LAhoANnOLlRcPpyV14gCSRBXTM7zcvtrn3fVeYYeLimhRTfVS_kaEF/s72-c/irssi.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6768017463404896606.post-4066347415083730859</id><published>2011-11-07T07:36:00.001+08:00</published><updated>2011-11-07T07:44:31.622+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ebookme"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="midp"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="samsung-e2230m"/><title type='text'>Read text file on non JSR-75 phone</title><content type='html'>So my &lt;a href=&quot;http://mobilezoo.biz/samsung_GT-E2230_specs&quot;&gt;Samsung GT-E2230M&lt;/a&gt; does not support &lt;a href=&quot;http://en.wikipedia.org/wiki/JSR-75&quot;&gt;JSR-75&lt;/a&gt; which is Java API on mobile devices that allow access to the device&#39;s file system such as the SD card. This mean any midp application in the phone can&#39;t &amp;nbsp;simply open arbitrary text files and also mean I can&#39;t use Opera Mini save webpage feature.&lt;br /&gt;
&lt;br /&gt;
Searching around, I found &lt;a href=&quot;http://sourceforge.net/projects/ebookme/&quot;&gt;EbookMe&lt;/a&gt; application that allow me to package a text files into a complete midp application to be installed on the phone just like any java apps on the phone. It a little bit cumbersome but at least now I have something to read while in waiting and away from my laptop and Internet connection.&lt;br /&gt;
&lt;br /&gt;
Using it quite simple, provided you have Java properly setup on your system.&lt;br /&gt;
&lt;pre&gt;sh ebookme.sh -Dname=&quot;Whatever Prog&quot; -Dout=whatever whatever.txt
&lt;/pre&gt;
The unzipped tarball provide a wrapper script ebookme.sh that you can run by simply passing the text file name as parameter. The -Dname and -Dout is optional to customize the name of generated midp app. The command would produce 2 files - whatever.jad, whatever.jar. You then transfer whatever.jar to your mobile phone and install it like a normal midp application.&lt;br /&gt;
&lt;br /&gt;
Another limitation of the phone is it can&#39;t open anything more than 200KB. This mean for large text such as the free book &lt;a href=&quot;http://www.artofcommunityonline.org/get/&quot;&gt;The Art of Community&lt;/a&gt; by Jono Bacon I had to convert the pdf into text first and then use unix split to split it into multiple files.&lt;br /&gt;
&lt;br /&gt;
Recently, I&#39;d also found &lt;a href=&quot;https://github.com/aaronsw/html2text&quot;&gt;html2text&lt;/a&gt; python lib that allow you to convert any HTML page into text file formatted in markdown. This make reading the text on the small phone screen much pleasurable.</content><link rel='replies' type='application/atom+xml' href='http://metak4ml.blogspot.com/feeds/4066347415083730859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6768017463404896606/4066347415083730859?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/4066347415083730859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6768017463404896606/posts/default/4066347415083730859'/><link rel='alternate' type='text/html' href='http://metak4ml.blogspot.com/2011/11/read-text-file-on-non-jsr-75-phone.html' title='Read text file on non JSR-75 phone'/><author><name>Kamal Mustafa</name><uri>http://www.blogger.com/profile/02858510377046461486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>