<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[@Dev Blog]]></title><description><![CDATA[Tech blogs from The @ Company related to the @protocol: an Open-source, P2P Internet protocol that enables people, developers and enterprises to handle personal data based on trust and permissions.]]></description><link>https://blog.atsign.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1615268390126/8qgUus49o.png</url><title>@Dev Blog</title><link>https://blog.atsign.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 15 Sep 2026 14:25:31 GMT</lastBuildDate><atom:link href="https://blog.atsign.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Google Cloud Custom Metrics]]></title><description><![CDATA[For a long while we ran The atPlatform on Ubuntu Linux, but we found that doing stuff with nodes on a large Docker Swarm spiked CPU usage for cloud-init. Every start or stop of a container was a change to networking, and every change to networking ca...]]></description><link>https://blog.atsign.dev/google-cloud-custom-metrics</link><guid isPermaLink="true">https://blog.atsign.dev/google-cloud-custom-metrics</guid><category><![CDATA[google cloud]]></category><category><![CDATA[monitoring]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Fri, 25 Mar 2022 14:37:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648218194390/9HxfmiT6_.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For a long while we ran The atPlatform on Ubuntu Linux, but we found that doing stuff with nodes on a large Docker Swarm spiked CPU usage for cloud-init. Every start or stop of a container was a change to networking, and every change to networking caused cloud-init to burn CPU (usually at times when the CPU should be busy on other things).</p>
<p>So we started the shift to <a target="_blank" href="https://www.flatcar.org/">Flatcar Linux</a>, a container focussed distribution that picked up what CoreOS had been doing before RedHat decided to kill it off.</p>
<p>But there was a problem. We were using the <a target="_blank" href="https://cloud.google.com/stackdriver/docs/solutions/agents/ops-agent">Google Ops Agent</a> to get metrics from inside our Ubuntu VMs, and Flatcar isn't one of the <a target="_blank" href="https://cloud.google.com/stackdriver/docs/solutions/agents/ops-agent#supported_operating_systems">supported distros</a>. I raised <a target="_blank" href="https://github.com/flatcar-linux/Flatcar/issues/560">an issue</a> with the Flatcar team, but it quickly became clear that Ops Agent wasn't a good fit for their approach.</p>
<p>Meanwhile we noticed that our monitoring bill was HUGE. It turns out that the default for Ops Agent is to monitor every process (in detail), which can get expensive when you're running hundred to thousands of containers per node :0</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648218194390/9HxfmiT6_.jpg" alt="ops_agent.jpg" /></p>
<p>We shut down Ops Agent, and briefly replaced it with a 'ghetto' monitoring script. But this was the push we needed to get a custom metrics solution in place.</p>
<p>Of course there's an API for Stackdriver, and like most things Google Cloud there's good support for Python and Golang SDKs. A quick search brought me to Arpana Mehta's <a target="_blank" href="https://medium.com/google-cloud/confused-with-custom-monitoring-metrics-on-gcp-c514cd4a776b">Getting started with Google Cloud Monitoring APIs</a>, which had the boilerplate code I needed to customise. After adding in CPU Load Average, I had our most crucial metric (and I returned to my script a few days later to add in root volume utilisation).</p>
<p>With a working script in the staging environment I then packaged it up (along with the google-cloud-monitoring package) into a Docker container that could be deployed across our Swarm nodes. After less than a day of 'ghetto' monitoring we were back to seeing everything we needed in our dashboard, with much lower ingestion and storage charges, as we're only sending the metrics we need rather than everything about everything.</p>
<p>The script, Dockerfile etc. are now open source - <a target="_blank" href="https://github.com/atsign-company/at_swarm_load">at_swarm_load</a>.</p>
<p>We've also found that the same approach can be used to monitor our CI/CD VMs on AWS. It's a little more fiddly, due to the need for service account files, but means we can use a single dashboard to keep an eye on things.</p>
]]></content:encoded></item><item><title><![CDATA[Run your own private cloud with dess in AWS]]></title><description><![CDATA[dess is the self-hosted option for secondary servers of an atSign, and a great tool for developers to debug their applications! If you're not sure what an atSign is check it out here! You can run several secondary servers on a single instance of dess...]]></description><link>https://blog.atsign.dev/run-your-own-private-cloud-with-dess-in-aws</link><guid isPermaLink="true">https://blog.atsign.dev/run-your-own-private-cloud-with-dess-in-aws</guid><category><![CDATA[AWS]]></category><category><![CDATA[@platform]]></category><category><![CDATA[Cloud]]></category><dc:creator><![CDATA[Tyler Trott]]></dc:creator><pubDate>Mon, 25 Oct 2021 16:45:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1635180278253/YnXDjcopP.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>dess is the self-hosted option for secondary servers of an atSign, and a great tool for developers to debug their applications! If you're not sure what an atSign is check it out <a target="_blank" href="https://atsign.com/what-is-an-sign/">here</a>! You can run several secondary servers on a single instance of dess, as each secondary scales to use 6MB of ram or less.</p>
<p>dess is an acronym which stands for:</p>
<p>d. Distributed e. Edge s. Secondary s. Server</p>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<ul>
<li><p>Registered atSign(s) to setup (get free/paid from <a target="_blank" href="https://my.atsign.com/go">here</a> )</p>
</li>
<li><p>An AWS account (sign up/in <a target="_blank" href="https://aws.amazon.com">here</a> )</p>
</li>
<li><p>A Fully Qualified Domain Name (FQDN)</p>
</li>
</ul>
<h3 id="heading-1-registering-your-atsign">1. Registering your atSign</h3>
<p>This topic is already well documented! Please follow the guidance of <a target="_blank" href="https://atsign.com/faqs/">The atPlatform FAQs</a> and register via <a target="_blank" href="https://my.atsign.com/go">the registrar site</a>.</p>
<h3 id="heading-2-sign-up-for-aws-account">2. Sign-up for AWS account</h3>
<p>If you are new to AWS, signing up for an account is free! The cost of running dess is about $10/month. You can create your account <a target="_blank" href="https://aws.amazon.com">here</a> and as of June 6th, 2021 there is a “free tier” available which was used during the original making of this guide which can be found on <a target="_blank" href="https://atsign.dev/docs/guides/dess-setup/dess-aws/">atsign.dev</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753486373/8NqOo5nXjX.png" alt="aws-free-trial.png" /></p>
<p>Follow the instructions on screen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753505424/62GzdgYSF.png" alt="aws-sign-up.png" /></p>
<p>Once you are done with registration, you will be able to login to your <a target="_blank" href="http://aws.amazon.com/console">aws console</a>.</p>
<p>Make sure that you select the correct region in the top right corner.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753520823/M9ttqipBx.png" alt="aws-region.png" /></p>
<p>Select the region that is geographically closest to your location for best performance.</p>
<p>You are now setup on AWS and ready to prepare dess.</p>
<h3 id="heading-3-register-your-own-fully-qualified-domain-name-fqdn">3. Register your own fully qualified domain name (FQDN)</h3>
<p>This step can be performed at a variety of sites, all with different pricing models. You can use sites like <a target="_blank" href="http://www.godaddy.com">Go Daddy</a>, <a target="_blank" href="https://www.namecheap.com/">Namecheap</a>, and many others. However, since we have AWS account, we can use it to register our domain through the Route 53 service.</p>
<p>In your AWS console navigate to services in top left corner and select <code>Route 53</code>.</p>
<h4 id="heading-a-register-domain-name-with-aws">a) Register domain name with AWS.</h4>
<p>In your AWS console search for <code>Route 53</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753543280/8mypOjFFn.png" alt="aws-route53.png" /></p>
<p>You can start looking for your domain directly from here:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753567018/De0K8li7Tw.png" alt="image-20210726083635919.png" /></p>
<p>Based on the domain name you search, AWS will give you similar options, and their pricing. As I am looking for the best deal, the .link domain seems like a good option at a cost of 5$ / year.</p>
<p>In my case 4atsign.link is free and I will register it by clicking “Add to cart” and continue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753587931/p1PtNEq0h.jpeg" alt="clip_image002.jpeg" /></p>
<p>Fill out DNS registration form:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753608099/DsiyRvBml.jpeg" alt="clip_image004.jpeg" /></p>
<p>Continue review details and order. At this point if all is fine you should see your domain request pending:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634753623593/cZa1nKDTp.jpeg" alt="clip_image006.jpeg" /></p>
<p>This can take some time so why don’t we move on to next step!</p>
<h3 id="heading-4-preparing-aws-instance">4. Preparing AWS instance</h3>
<p>Now since I am new to AWS, the easiest way to get started is by using the LightSail service from service catalog. This will enable you to deploy small system which is more then capable of handling dess at pre-set price.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178602223/_9xJOf70x.jpeg" alt="clip_image002-16272842283471.jpeg" /></p>
<p>Welcome to LightSail:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178622540/gNZc1gtWY.jpeg" alt="clip_image004-16272842283482.jpeg" /></p>
<p>First step is to create new instance. Fortunately, we have big orange button that can do just that!</p>
<p>There are several options we are presented at this moment. Since I am living in Europe I select “<strong>Frankfurt, Zone A</strong> (eu-central-1a)” as my instance location. Instance region will dictate how well will your instance response based on your geographical location. People located in India should selects APAC region where as people in US should select North America region. You can leave availability zone set as default.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178681600/JI1iVh6pz.jpeg" alt="clip_image006-16272842283483.jpeg" /></p>
<p>Next up will be selection of operating system we want to deploy. We know that dess works well with Ubuntu 20.04 LTS so let's select just that.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178712967/48WTgq7Ue.jpeg" alt="clip_image008.jpeg" /></p>
<p>You are presented with a couple more options, but unless you know what you are doing leave these as is.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178730584/hHG4uMghX.jpeg" alt="clip_image010.jpeg" /></p>
<p>Now let’s select an instance plan. dess is relatively light weight so for testing purposes I will select the first instance plan for $3.5/Month. This will provide us with 512 MB of RAM, 1vCPU, 20GB of storage and 1TB of data transfer. This is more than enough to run our dess.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178777770/7tP2tmEho.jpeg" alt="clip_image012.jpeg" /></p>
<p>Lastly we have to name our instance. This is the name you will be presented with in your dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178814636/wtElonVVEO.jpeg" alt="clip_image014.jpeg" /></p>
<p>Last but not least is to press “Create instance”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178830794/QjXTB826K.png" alt="clip_image015.png" /></p>
<p>After a couple of seconds you should be re-routed to your dashboard and see your instance up and running:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178845273/WuI1SAN4f.png" alt="clip_image016.png" /></p>
<h3 id="heading-5-preparing-your-instance-for-network-access">5. Preparing your instance for network access</h3>
<h4 id="heading-a-assignment-of-static-ip">a) Assignment of Static IP</h4>
<p>Next up, is to provide our instance with a static IP and linking our domain to it.</p>
<p>When you click on your instance name, it will take you to the management console, which should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178877152/nDEsjCjbI.jpeg" alt="clip_image002-16272853840264.jpeg" /></p>
<p>This is where you control hardware, connectivity and if needed can delete your instance.</p>
<p>Lets configure a static IP address for your new instance. Navigate to Networking and click on Create static IP:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178901218/ISRGI8l4n.png" alt="clip_image003.png" /></p>
<p>Our region and instance is selected, so the only thing left is to name our static IP. I selected the name atsign-static, but it can be any name you like.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178925342/sxV62JwDs.jpeg" alt="clip_image005.jpeg" /></p>
<p>Let's hit create:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178938269/6baa3-fvP.jpeg" alt="clip_image007.jpeg" /></p>
<p>And voila, we now have a static IP address on the internet and it will not change. Now we can link our domain name with it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178951751/--HBvSO4H.jpeg" alt="clip_image009.jpeg" /></p>
<p>When you click on your instance name and navigate to Networking, the static IP is now assigned.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178977958/WnWom-8TI.jpeg" alt="clip_image011.jpeg" /></p>
<h4 id="heading-b-assignment-of-domain-name-to-your-static-ip">b) Assignment of Domain name to your static IP</h4>
<p>We can now move on to linking our static IP address to our domain. This is done via the AWS console which can be accessed in the top right of Lightsail.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635178990770/ItDgCjBw3.png" alt="clip_image001.png" /></p>
<p>Verify your email used for registering domain:</p>
<p>By now you should receive a verification email that will confirm registration of your domain. Click this link before moving on.</p>
<p>Linking domain with your static address:</p>
<p>Let's navigate to “Route 53” from Services menu.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179028154/v1BquqEpx.jpeg" alt="clip_image003.jpeg" /></p>
<p>From your dashboard click on “Domain” which will take you to the “Registered Domains” tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179047271/HacTw9Mah.jpeg" alt="clip_image005-16272854399076.jpeg" /></p>
<p>Here you can click on your registered domain which will take you to overview page with domain status and contacts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179075160/4Y0fv3i2F.jpeg" alt="clip_image007-16272854399087.jpeg" /></p>
<p>Click on “Manage DNS”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179098880/GQb-3CYCA.jpeg" alt="clip_image009-16272854399088.jpeg" /></p>
<p>And click on your domain name.</p>
<p>This will show you your DNS records for your domain. We now need to link A type record to your domain linking it to IP address of your instance.</p>
<p>This is done simply by typing your static IP address from previous step into field “Value” and clicking Create record:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179116863/lXHuuQpjQ.jpeg" alt="clip_image011-16272854399089.jpeg" /></p>
<p>If everything goes well you should see following in your domain dashboard:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179128949/veFgi8hDG.jpeg" alt="clip_image013.jpeg" /></p>
<p>To test if you are successful open command line and ping your domain. You should see your instance static IP address. It will not respond which is normal due to IPv4 firewall. It is actually good thing!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179143299/jwCUqx4cC.png" alt="clip_image014.png" /></p>
<p>At this point we have created a DNS record we will use to link our dess. We created an instance name which will be running our dess and we have opened a port range which is exposed to the internet and we can communicate with the atSign root server and our apps.</p>
<h4 id="heading-c-setting-up-firewall">c) Setting up Firewall</h4>
<p>Next up, we need to make sure we have ports open for our dess to communicate with the root server and our apps. In Section networking go to section “IPv4 Firewall” and click “+ Add rule”. Our rule will be “Custom” on TCP protocol with Port range in a number higher than 1024. In my case, I have selected port range 8000-8010. This will enable me to run up to 10 atSigns in parallel.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179164229/wrL50Jqqf.jpeg" alt="clip_image002-16272854074665.jpeg" /></p>
<p>Click create and verify that your new rule is in list:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179164229/wrL50Jqqf.jpeg" alt="clip_image002-16272854074665.jpeg" /></p>
<h3 id="heading-6-instance-setup-and-dess-deployment">6. Instance setup and dess deployment</h3>
<p>Open your <a target="_blank" href="https://lightsail.aws.amazon.com/">LightSail console</a></p>
<p>By now you should see your instance in “Running state”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179196090/oLX4HhULZ.jpeg" alt="clip_image002-162728546025210.jpeg" /></p>
<p>Open it and on the tab Connect click on “Connect using SSH”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179209059/ef1C4-HGT.png" alt="clip_image003-162728546025211.png" /></p>
<p>You should be presented by a new window with command line:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179222535/EUXedcXqs.jpeg" alt="clip_image005-162728546025212.jpeg" /></p>
<p>Before we do anything else, we should update the system:</p>
<pre><code class="lang-plaintext">sudo apt update &amp;&amp; sudo apt upgrade
</code></pre>
<p>This might take some time, but it will make sure we have latest repository information and the system is up-to-date.</p>
<p>Next make sure curl is installed, we will use curl to pull the dess installation file:</p>
<pre><code class="lang-plaintext">sudo apt install curl
</code></pre>
<p>Finally, run the dess installer:</p>
<pre><code class="lang-plaintext">curl -fsSL https://getdess.atsign.com | sudo bash
</code></pre>
<p>Once the installer is finished you should be prompted like so:</p>
<pre><code class="lang-plaintext">Dess installed, please move on to the sudo dess-create command.
</code></pre>
<h3 id="heading-7-registration-of-atsign-in-your-private-dess">7. Registration of atSign in your private dess</h3>
<p>At this step, you should already have your atSign registered at http://atsign.com. If not <strong>go do it!</strong></p>
<p>I have registered my own free atSign (@ 44likelycanary) which I will link to my dess.</p>
<p>In your instance console, navigate to dess folder.</p>
<p>We now need to create the service that will host our atSign by executing the dess-create command:</p>
<pre><code class="lang-plaintext">$ sudo dess-create @44likelycanary 4atsign.link 8000 &lt;email address&gt; likelycanary
</code></pre>
<p>To make it more understandable:</p>
<p>I will be registering my atSign <strong>@44likelycanary</strong>.</p>
<p>I will be using my domain <strong>4atsign.link</strong> which I have registered through AWS.</p>
<p>I am using port <strong>8000</strong> which I have opened in my instance firewall.</p>
<p>My registration email address is <strong>&lt;email address&gt;</strong> (this email is used to sign the SSL certificates).</p>
<p>The last <strong>likelycanary</strong> is the name that docker will use to track the service.</p>
<p>If everything is successful you should see output like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179270876/maXkG1601.jpeg" alt="clip_image004-162728549379914.jpeg" /></p>
<p>At this moment your atSign is registered on your dess.</p>
<h2 id="heading-8-activation-of-atsign">8. Activation of atSign</h2>
<p>Next up we need to activate it</p>
<p>Login to your dashboard <a target="_blank" href="https://my.atsign.com/dashboard">here</a></p>
<p>Open “my atSigns”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179311802/XBr0ZWmVj.png" alt="clip_image001-162728550968115.png" /></p>
<p>Open “managed” of atSign you are registering”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179533975/ZypX-cJs2.jpeg" alt="clip_image003-162728550968116.jpeg" /></p>
<p>Navigate to Advance settings:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179593518/J7zHzKeUo.jpeg" alt="clip_image005-162728550968117.jpeg" /></p>
<p>If you have already activated your atSign you will be prompted to erase all your data first</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179623471/-1-VQA8Io.jpeg" alt="clip_image007-162728550968118.jpeg" /></p>
<p>Once done you are able to link your atSign with your private dess. Use your domain and port number with which you have created service on your cloud instance and press Activate.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179637637/1LpmwUyra.jpeg" alt="clip_image009-162728550968119.jpeg" /></p>
<p>You should see that your atSign is being activated in your dashboard:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179657985/9ZmdlZGOf.jpeg" alt="clip_image011-162728550968120.jpeg" /></p>
<p>This can take several minutes so go get a cup of coffee, some tea maybe, stretch your body and pray you haven’t made any mistakes!</p>
<p>Once the activation process completes you are welcomed by green Activated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635179694031/P8Y-G4OyC.jpeg" alt="clip_image013-162728550968121.jpeg" /></p>
<p>You can now open atBuzz or atWavi and register your atSign via QR code and generate your keys!</p>
<p><strong>CONGRATULATIONS YOU'RE FINISHED!</strong></p>
]]></content:encoded></item><item><title><![CDATA[Secure Flutter object storage/sharing with the atPlatform]]></title><description><![CDATA[Do you wish to store and share objects on Flutter without worrying about databases, infrastructure and other annoyances? What would these objects look like and how would you make them? The atPlatform already has you covered! Let’s take a deep dive in...]]></description><link>https://blog.atsign.dev/secure-flutter-object-storagesharing-with-the-atplatform</link><guid isPermaLink="true">https://blog.atsign.dev/secure-flutter-object-storagesharing-with-the-atplatform</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Android]]></category><category><![CDATA[@platform]]></category><category><![CDATA[Flutter Examples]]></category><dc:creator><![CDATA[Tyler Trott]]></dc:creator><pubDate>Mon, 18 Oct 2021 19:59:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1634586906627/MPA7k88uq.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do you wish to store and share objects on Flutter without worrying about databases, infrastructure and other annoyances? What would these objects look like and how would you make them? The atPlatform already has you covered! Let’s take a deep dive into the AtKey object class and its attributes.</p>
<p>The AtKey object is stored on an atSign’s personal secondary server (a separate article written on secondary servers), so dealing with databases is not a worry on the atPlatform. There are a few distinct attributes that exist within an AtKey.</p>
<pre><code class="lang-dart"><span class="hljs-built_in">String?</span> Key;
</code></pre>
<p>The key is the title or name of the AtKey object itself, may this be a recipe name (like mom’s secret recipe from at_cookbook), or maybe even the name of a weather update (like, cloudy or sunny).</p>
<pre><code class="lang-dart"><span class="hljs-built_in">String?</span> sharedWith;
</code></pre>
<p>The sharedWith attribute is as easy to understand as it is to read! This is simply the atSign in which we wish to share this AtKey object with, or who the AtKey object has already been shared with!</p>
<pre><code class="lang-dart"><span class="hljs-built_in">String?</span> sharedBy;
</code></pre>
<p>Similar to sharedWith, sharedBy is just who the creator or original sender of the AtKey object is.</p>
<pre><code class="lang-dart"><span class="hljs-built_in">String?</span> Namespace;
</code></pre>
<p>The namespace is the name of the app where the AtKey object was created. This is why passing the regular expression to retrieve AtKey objects from specific applications is a possibility.</p>
<pre><code class="lang-dart">Metadata? Metadata;
</code></pre>
<p>Metadata, which is commonly known as the data about the data, will be described in its own video series as there is much to it!</p>
<p>Taking a look at one of the <a target="_blank" href="https://github.com/atsign-foundation">atPlatform’s demo application</a>, we can see the AtKey object in action. The <a target="_blank" href="https://github.com/atsign-foundation/at_demos/tree/trunk/at_cookbook">at_cookbook</a> app allows a testable or production level atSign to create a recipe with a name, description, list of ingredients, and a picture. After doing this, the atSign is capable of sharing this entire recipe with another testable atSign. A dishWidget class is created which encapsulates an individual recipe with each instance. Its parameters are defined below:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DishWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWiatdget</span> </span>{
 <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
 <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> ingredients;
 <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> description;
 <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> imageURL;
 <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> prevScreen;

 DishWidget({
   <span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.title,
   <span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.ingredients,
   <span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.description,
   <span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.imageURL,
   <span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.prevScreen,
});
...
</code></pre>
<p>After we pull dishWidgets that exist in a secondary server, we display them on the home screen, where the list of recipes are located.</p>
<p>Now that you have the recipe itself, you’re able to pass this through the notify verb. When an atSign wishes to share a recipe, the _share function will be called. This function is structured as so:</p>
<pre><code class="lang-dart">_share(BuildContext context, <span class="hljs-built_in">String</span> sharedWith) <span class="hljs-keyword">async</span> {
 <span class="hljs-keyword">if</span> (sharedWith != <span class="hljs-keyword">null</span>) {
   AtKey lookup = AtKey()
     ..key = widget.dishWidget.title
     ..sharedWith = atSign;

   <span class="hljs-built_in">String</span> value = <span class="hljs-keyword">await</span> _serverDemoService.<span class="hljs-keyword">get</span>(lookup);

   <span class="hljs-keyword">var</span> metadata = Metadata()..ttr = <span class="hljs-number">-1</span>;
   AtKey atKey = AtKey()
     ..key = widget.dishWidget.title
     ..metadata = metadata
     ..sharedBy = atSign
     ..sharedWith = _otherAtSign;

   <span class="hljs-keyword">var</span> operation = OperationEnum.update;
   <span class="hljs-keyword">await</span> _serverDemoService.notify(atKey, value, operation);
   Navigator.pop(context);
 }
}
</code></pre>
<p>Here, we’re initializing a variable called lookup as an AtKey object (lookup will be our recipe). You’ll notice that we’re defining a couple of the attributes of the AtKey object (mainly the name of the recipe and what atSign is this recipe being shared with).</p>
<p>We would like to “get” the values of our recipe, so we use the get verb from the serverDemoService file of the application. ttr (Time To Refresh), a metadata attribute, is called with a value of -1, which means that we’re confident that the values of our recipe won’t change. Once the recipe has been cached on the secondary server that has received the recipe, it will not need to worry about updating its values at any point.</p>
<p>After passing all of the necessary values such as the metadata, the appropriate atSigns, and the type of notification we’d like to send, we simply pass the values through the notify verb!</p>
]]></content:encoded></item><item><title><![CDATA[The Zen of CI]]></title><description><![CDATA[TL;DR
A colleague recently suggested a change that I thought wouldn't work, so my first response was 'you can waste your time trying that'. But then I realised that the change itself was trivial, and I didn't need to spend time setting up a test envi...]]></description><link>https://blog.atsign.dev/the-zen-of-ci</link><guid isPermaLink="true">https://blog.atsign.dev/the-zen-of-ci</guid><category><![CDATA[github-actions]]></category><category><![CDATA[ci]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Dart]]></category><category><![CDATA[TLS]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Fri, 08 Oct 2021 12:44:35 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-tldr">TL;DR</h2>
<p>A colleague recently suggested a change that I thought wouldn't work, so my first response was 'you can waste your time trying that'. But then I realised that the change itself was trivial, and I didn't need to spend time setting up a test environment, I could just raise a pull request and wait for the continuous integration (CI) tests to fail.</p>
<p>Only the tests didn't fail. My colleague's approach was valid. But best of all neither of us had spent much time on it, the existing test suite did all the work.</p>
<h2 id="heading-background">Background</h2>
<p>For each pull request for <a target="_blank" href="https://github.com/atsign-foundation/at_server">at_server</a>, which is the core implementation of <a target="_blank" href="https://atsign.dev/docs/">The atPlatform</a> we run a test suite that consists of unit tests, functional tests and end to end tests. The end to end tests had started failing, which was weird, as the only recent changes were to docs, so nothing had changed in the implementation.</p>
<p>A dive into the logs on the CI virtual machines showed certificate errors, specifically that the certificates were out of date. This was also weird, as the certificates are automatically renewed by <a target="_blank" href="https://certbot.eff.org/">Certbot</a>, and were quite clearly in date. I could even validate that on the command line.</p>
<p>Then I remembered that LetsEncrypt had notified about an expiring legacy Root Certificate Authority (CA) certificate. But surely that wouldn't affect us... we definitely had the latest CA certs in place.</p>
<p>It turns out the expiration of DST Root CA X3 did affect us though, because LetsEncrypt had chosen defaults that were meant to help with older systems, but had the unintended consequence of causing a bunch of collateral damage.</p>
<p>Picking through the chain of trust in <code>fullchain.pem</code>... Our nice in date certs for the CI infrastructure were issued by the LetsEncrypt R3 intermediate CA, and the R3 was issued by the ISRG root CA X1. We had ISRG X1 in our cacerts, so all should be good. Well, no actually, because the ISRG root CA X1 was signed by DST root CA X3, which expired on 30 Sep. So the Dart HTTP package was walking along the chain of trust, getting to the expired root, and returning an error.</p>
<p>Our immediate workaround was to cut the ISRG root CA X1 (signed by DST root CA X3) out of fullchain.pem (something that can be achieved by invoking Certbot with an optional flag: <code>certbot renew --force-renewal --preferred-chain "ISRG Root X1"</code>). But workarounds can come back to bite later, and that particular one is likely to cause trouble when LetsEncrypt switch to their X2 CA in due course.</p>
<p>If you've an appetite for more gory details on why things weren't working then check out <a target="_blank" href="https://github.com/dart-lang/http/issues/627#issuecomment-936304689">this comment</a> on the GitHub issue for "HandshakeException: Handshake error in client (OS Error: CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate".</p>
<h2 id="heading-my-colleagues-suggestion">My colleague's suggestion</h2>
<blockquote>
<p>What if we just delete DST X3 from cacerts?</p>
</blockquote>
<p>I totally didn't expect this to work. What I thought would happen is Dart HTTP would walk the chain of trust to the DST root CA X3, find it missing from cacerts and return an error based on that.</p>
<p>I also thought about how long it would take me to set up a test environment to prove that it wouldn't work.</p>
<h2 id="heading-but-i-already-had-a-test-environment">But I already had a test environment</h2>
<p>All I needed to do was implement my colleague's (doomed to fail) suggestion, and let the CI take care of it.</p>
<p>I had to back out the fullchain.pem workaround on the CI VMs, but that took seconds (just like making the change and submitting the PR that would send it into CI).</p>
<p>It would only be a few minutes before I could watch the tests fail again, then smugly tell my colleague that his suggestion didn't work.</p>
<p>Except the tests passed. It turns out that Dart HTTP is smart enough to figure out a correct chain of trust to a valid CA in cacerts.</p>
<p>Hurray though, no need for a workaround any more.</p>
<h2 id="heading-this-is-making-me-rethink-my-testing-philosophy">This is making me rethink my testing philosophy</h2>
<p>How often have I been building local test harnesses when I should just send stuff through CI (even if it means adding more tests to the CI, which is generally a good thing)?</p>
<p>How often have I not even tried something in the expectation that it will fail, when it just might work, and the CI means I don't have to expend much effort to find out?</p>
]]></content:encoded></item><item><title><![CDATA[Exporting GitHub Projects (beta) with GraphQL and Python]]></title><description><![CDATA[GitHub recently launched a  beta for their new project boards, which provide a lot more flexibility than the old Kanban boards. I've been excited about the new functionality ever since I heard of the 'Memex' project.
With the new boards comes a new A...]]></description><link>https://blog.atsign.dev/exporting-github-projects-beta-with-graphql-and-python</link><guid isPermaLink="true">https://blog.atsign.dev/exporting-github-projects-beta-with-graphql-and-python</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[Python]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Tue, 14 Sep 2021 13:29:43 GMT</pubDate><content:encoded><![CDATA[<p>GitHub recently launched a  <a target="_blank" href="https://github.com/features/issues/">beta for their new project boards</a>, which provide a lot more flexibility than the old Kanban boards. I've been excited about the new functionality ever since I heard of the 'Memex' project.</p>
<p>With the new boards comes a new API, and like  <a target="_blank" href="https://docs.github.com/en/graphql/guides/using-the-graphql-api-for-discussions">Discussions </a> before it there's no REST API as GitHub continues the journey into it's v4 GraphQL API. So things are a little different, but they can also be made to work much the same as before.</p>
<p>I previously wrote about <a target="_blank" href="https://blog.atsign.dev/planning-poker-without-copypaste-ckpfmtucu05a63as1gzwvhviv">Planning Poker, without Copy/Paste</a> where we used some Python scripts '<a target="_blank" href="https://github.com/atsign-company/dump_cards">dump_cards</a>' to scrape data from the REST API. The key functions of those scripts were refactored into an <code>atdumpcards.py</code> module, leaving the scripts themselves as thin wrappers. To work with Projects(beta) we needed a new module <code>atdumpmemex.py</code> to construct <a target="_blank" href="https://docs.github.com/en/issues/trying-out-the-new-projects-experience/using-the-api-to-manage-projects">GraphQL queries</a> and parse the responses. The rest of this post will walk through that module to explain what's going on...</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/env python3</span>
<span class="hljs-keyword">import</span> base64, json, os, requests
</code></pre>
<p>One more import than before for base64, which we'll come to later...
Requests is used to send a request to the API, the response contains JSON, and the OS is being used to write a (.csv) file.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Color constants</span>
<span class="hljs-comment"># Reference: https://gist.github.com/chrisopedia/8754917</span>
COLERR=<span class="hljs-string">"\033[0;31m"</span>
COLINFO=<span class="hljs-string">"\033[0;35m"</span>
COLRESET=<span class="hljs-string">"\033[m"</span>
</code></pre>
<p>For colourful error messages.</p>
<pre><code class="lang-python">graphqlurl = <span class="hljs-string">'https://api.github.com/graphql'</span>
token = os.environ[<span class="hljs-string">'GITHUB_API_TOKEN'</span>]
headers = {<span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>, 
    <span class="hljs-string">"Accept"</span>: <span class="hljs-string">"application/json"</span>,
    <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">"Bearer "</span> + token,
    <span class="hljs-string">"GraphQL-Features"</span>: <span class="hljs-string">"projects_next_graphql"</span> }
</code></pre>
<p>This is where the GraphQL changes kick in. There's a single API end point for all queries. The authentication token also needs to be passed as a Bearer Token, and the GraphQL features for 'Project Next' need to be enabled.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_memex_projects</span>(<span class="hljs-params">org</span>):</span>
    query = (<span class="hljs-string">'query{ organization(login: \\"'</span> + org + <span class="hljs-string">'\\") '</span>
        <span class="hljs-string">'{ projectsNext(first: 20) { nodes { id title } } } }'</span>)
    response = requests.post(graphqlurl, 
        headers=headers, 
        data=<span class="hljs-string">'{"query": '</span>+<span class="hljs-string">'\"'</span> + query + <span class="hljs-string">'\"}'</span>)
    <span class="hljs-keyword">if</span> response.status_code != <span class="hljs-number">200</span>:
        <span class="hljs-comment"># An error occured</span>
        print(COLERR + <span class="hljs-string">"Error getting project list : "</span>
            + str(response.status_code) + <span class="hljs-string">" "</span> + response.text + COLRESET)
</code></pre>
<p>This function lists the projects in a given org, and starts by constructing a GraphQL query to to just that. The query is filtering by organization based on login, and then retrieving the first 20 projects with the ID and title for each. That query is then POSTed to the API end point, and the response is captured. If the response code is something other than 200 then there's a problem and an error is output.</p>
<pre><code class="lang-python">    json_projects = json.loads(response.text)
    <span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> json_projects[<span class="hljs-string">"data"</span>][<span class="hljs-string">"organization"</span>][<span class="hljs-string">"projectsNext"</span>][<span class="hljs-string">"nodes"</span>]:
        project_id = base64.b64decode(node[<span class="hljs-string">"id"</span>]).decode(<span class="hljs-string">"utf-8"</span>)
        print(<span class="hljs-string">f'<span class="hljs-subst">{project_id}</span>  <span class="hljs-subst">{node[<span class="hljs-string">"title"</span>]}</span>'</span>)
</code></pre>
<p>A json_projects dictionary is then created from loading the JSON contained in the text of the API request. The JSON produced by the GraphQL API is quite deeply nested, so the function iterates over the nodes contained in the data:organization:projectsNext:nodes part of the response.</p>
<p>The GraphQL IDs look something like <code>MDEyOlByb2plY3ROZXh0MzQ1Ng==</code> which is a base64 encoded representation of <code>012:ProjectNext3456</code>. The latter is easier on the eye, so the function decodes the ID into bytes, and decodes those into a string. The ID and title for each project can then be printed to the terminal.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_memex_columns</span>(<span class="hljs-params">project_id</span>):</span>
    b64id = base64.b64encode(project_id.encode(<span class="hljs-string">"ascii"</span>)).decode(<span class="hljs-string">"utf-8"</span>)
    query = (<span class="hljs-string">'query{ node(id: \\"'</span> + b64id + <span class="hljs-string">'\\")  '</span>
        <span class="hljs-string">'{ ... on ProjectNext { fields(first: 20) '</span>
        <span class="hljs-string">'{ nodes { id name settings } } } } }'</span>)
</code></pre>
<p>The list_memex_columns function works along similar lines. Except this time it's constructing a query to get the ID, name and settings from a given project.</p>
<pre><code class="lang-python">    <span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> json_nodes[<span class="hljs-string">"data"</span>][<span class="hljs-string">"node"</span>][<span class="hljs-string">"fields"</span>][<span class="hljs-string">"nodes"</span>]:
        <span class="hljs-keyword">if</span> node[<span class="hljs-string">"name"</span>] == <span class="hljs-string">"Status"</span>:
            json_status = json.loads(node[<span class="hljs-string">"settings"</span>])
            <span class="hljs-keyword">for</span> options <span class="hljs-keyword">in</span> json_status[<span class="hljs-string">"options"</span>]:
                print(<span class="hljs-string">f'<span class="hljs-subst">{options[<span class="hljs-string">"id"</span>]}</span> <span class="hljs-subst">{options[<span class="hljs-string">"name"</span>]}</span>'</span>)
</code></pre>
<p>Within settings it then drills down for nodes named 'Status' to get the JSON payload that contains the column IDs and names.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_memex_cards</span>(<span class="hljs-params">column_id, project_id</span>):</span>
    cards_file = column_id + <span class="hljs-string">".csv"</span>
    b64id = base64.b64encode(project_id.encode(<span class="hljs-string">"ascii"</span>)).decode(<span class="hljs-string">"utf-8"</span>)
    query = (<span class="hljs-string">'query{ node(id: \\"'</span> + b64id + <span class="hljs-string">'\\") '</span>
        <span class="hljs-string">'{ ... on ProjectNext { items(first: 100) '</span>
        <span class="hljs-string">'{ nodes{ title fieldValues(first: 8) { nodes{ value } } '</span>
        <span class="hljs-string">'content{ ...on Issue { number labels(first: 50) '</span>
        <span class="hljs-string">'{ nodes{ name } } } } } } } } }'</span>)
</code></pre>
<p>With the ID in hand a query can then be used to grab the issues associated with a column.</p>
<p>Unfortunately the GraphQL API doesn't yet provide for filtering within queries, so it has to be done in the script:</p>
<pre><code class="lang-python">    json_cards = json.loads(response.text)
    f = open(cards_file, <span class="hljs-string">'w'</span>)
    f.write(<span class="hljs-string">"Issue Key,Summary,Description,Acceptance Criteria,Story Points\n"</span>)
    <span class="hljs-keyword">for</span> card <span class="hljs-keyword">in</span> json_cards[<span class="hljs-string">"data"</span>][<span class="hljs-string">"node"</span>][<span class="hljs-string">"items"</span>][<span class="hljs-string">"nodes"</span>]:
        <span class="hljs-keyword">for</span> status <span class="hljs-keyword">in</span> card[<span class="hljs-string">"fieldValues"</span>][<span class="hljs-string">"nodes"</span>]:
            <span class="hljs-keyword">if</span> status[<span class="hljs-string">"value"</span>] == column_id:
                f.write(<span class="hljs-string">f'<span class="hljs-subst">{card[<span class="hljs-string">"content"</span>][<span class="hljs-string">"number"</span>]}</span>,<span class="hljs-subst">{card[<span class="hljs-string">"title"</span>]}</span>,,,'</span>)
                <span class="hljs-keyword">for</span> label <span class="hljs-keyword">in</span> card[<span class="hljs-string">"content"</span>][<span class="hljs-string">"labels"</span>][<span class="hljs-string">"nodes"</span>]:
                        <span class="hljs-keyword">if</span> (label[<span class="hljs-string">"name"</span>][<span class="hljs-number">-2</span>:]==<span class="hljs-string">"SP"</span>):
                            f.write (<span class="hljs-string">f'<span class="hljs-subst">{label[<span class="hljs-string">"name"</span>].partition(<span class="hljs-string">" "</span>)[<span class="hljs-number">0</span>]}</span>'</span>)
                            <span class="hljs-comment"># break loop in case there are multiple SP labels</span>
                            <span class="hljs-keyword">break</span>
                f.write (<span class="hljs-string">'\n'</span>)
    f.close
</code></pre>
<p>The function uses a set of nested loops and conditionals to first extract just the issues in the desired column, and then any story points from labels on those issues. The extracted data is written out to a .csv file that can then be imported into Planning Poker.</p>
]]></content:encoded></item><item><title><![CDATA[Implement an easy login system for your next Flutter app]]></title><description><![CDATA[TLDR?
** Watch our YouTube video that covers this article! **
How to implement at_onboarding_flutter:
Are you tired of setting up FireBase tables, handling usernames and passwords, and writing line after line of code setting up authentication methods...]]></description><link>https://blog.atsign.dev/implement-an-easy-login-system-for-your-next-flutter-app</link><guid isPermaLink="true">https://blog.atsign.dev/implement-an-easy-login-system-for-your-next-flutter-app</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[@platform]]></category><category><![CDATA[Android]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Tyler Trott]]></dc:creator><pubDate>Fri, 10 Sep 2021 22:15:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631311782923/9vGfRdJZV.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-tldr">TLDR?</h2>
<p>** Watch our <a target="_blank" href="https://www.youtube.com/watch?v=SiYZ8FBJUKI">YouTube video</a> that covers this article! **</p>
<h3 id="heading-how-to-implement-atonboardingflutter">How to implement at_onboarding_flutter:</h3>
<p>Are you tired of setting up FireBase tables, handling usernames and passwords, and writing line after line of code setting up authentication methods and features?</p>
<p>Atsign has designed a platform (titled the atPlatform) which houses many packages that make the life of a developer easier! Let’s take a deep dive into one of these that greatly assists in authentication!</p>
<p>What you’re looking for is called at_onboarding_flutter! You can find it here, on <a target="_blank" href="https://pub.dev/packages/at_onboarding_flutter">pub.dev</a>. You can quickly add it to your dependencies within your pubspec.yaml file like this (<strong>note: be sure that you check pub.dev for the most recent version!</strong>):</p>
<pre><code class="lang-plaintext">dependencies:
  at_onboarding_flutter: ^2.1.2
</code></pre>
<p>After successfully running the ‘pub get’ command which is when Flutter saves the concrete package version in your project, you can import its package within one of your files. This should look something like this:</p>
<pre><code class="lang-plaintext">import 'package:at_onboarding_flutter/at_onboarding_flutter.dart';
</code></pre>
<p>Congrats! After doing this, you’re already halfway to have an atSign authenticate into your application! You’ll need to pass in a few parameters such as; your build context, the current atClientPreference (covered in another article), your application’s domain (there are currently two that exist, one for production level which is ‘root.atsign.org’ and one for testing purposes - ‘vip.ve.atsign.zone’). You can also pass the color of your app as there is included U.I. with at_onboarding_flutter! You can then onboard by passing in the current atClientpreference and atSign you wish to authenticate to the current client instance. You can declare your own personalised successful or error log message, and be sure to state which screen the atSign will be navigated to upon successfully onboarding. Below, you will find what this may look like in code:</p>
<pre><code class="lang-plaintext">Onboarding(
context: context,
atClientPreference: atClientPreference,
domain: 'root.atsign.org' or 'vip.ve.aatsign.zone',
appColor: Color.fromARGB(255, 240, 94, 62),
appAPIKey: 'your-api-key-here',
onboard: (value, atsign) {
ClientSdkService.getInstance().atsign = atsign;
ClientSdkService.getInstance().atClientServiceMap = value;
ClientSdkService.getInstance().atClientServiceInstance = value[atsign];
_logger.finer('Successfully onboarded $atsign');
},
onError: (error) {
_logger.severe('Onboarding throws $error error');
},
nextScreen: HomeScreen(),
);
</code></pre>
<p>Just a few notes for the above code. I am calling some functions that exist in a file called ClientSdkService.dart. This file contains many useful functions that are pre-coded by the atPlatform core developers. To include this file in your own project (you’ll be able to explore than many other useful functions!), feel free to navigate to the atFoundation <a target="_blank" href="https://github.com/atsign-foundation/at_demos/tree/trunk">at_demos repository</a>, which is open-source, on Github! The ClientSdkService file can be found in any of these demo applications under the same file path (or roughly the same) -&gt; /complete_version/lib/service/.</p>
<p>Making reference to the demo applications, be sure to check out how the demo apps utilize at_onboarding_flutter!</p>
<p>If you wish to learn more about at_onboarding_flutter or maybe the atPlatform in its entirety, check it out on <a target="_blank" href="pub.dev/at_onboarding_flutter">pub.dev</a> and explore on <a target="_blank" href="https://atsign.dev">atsign.dev</a> and <a target="_blank" href="https://atsign.com">atsign.com</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Integrate an Event Planning package within your next Flutter Project with the atPlatform!]]></title><description><![CDATA[Like how we regularly share calendar items between our friends, family, and colleagues, it would be very convenient to have a data structure for events that works with the atPlatform. This is where the at_events_flutter dependency comes into play.
Be...]]></description><link>https://blog.atsign.dev/integrate-an-event-planning-package-within-your-next-flutter-project-with-the-atplatform</link><guid isPermaLink="true">https://blog.atsign.dev/integrate-an-event-planning-package-within-your-next-flutter-project-with-the-atplatform</guid><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[@platform]]></category><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><dc:creator><![CDATA[Damien Bahati]]></dc:creator><pubDate>Thu, 12 Aug 2021 16:35:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628786024696/eB3dl5yjw.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Like how we regularly share calendar items between our friends, family, and colleagues, it would be very convenient to have a data structure for events that works with the atPlatform. This is where the at_events_flutter dependency comes into play.</p>
<p>Be sure to check our <a target="_blank" href="https://pub.dev/packages/at_events_flutter">pub.dev</a> to see the most up-to-date version at_events_flutter!</p>
<p>The first step, is the initialization of the event service. For demonstration purposes, in this article, we’ll be examining snippets from the example app in the <a target="_blank" href="https://github.com/atsign-foundation/at_widgets/tree/trunk/at_events_flutter">at_events_flutter Github directory</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628089998157/kRL780eyf.png" alt="Screenshot 2021-08-04 111019.png" /></p>
<p>The most paramount function in the at_event_flutter library is CreateEvent(), which builds an instance of a CreateEvent that can be customized and shared across different atSigns as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090031099/tOfI61DdC.png" alt="Screenshot 2021-08-04 111046.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090060761/PoacUU78r.png" alt="Screenshot 2021-08-04 111106.png" /></p>
<p>CreateEvent() is a separate screen in the application that guides an individual through a list of fields they can populate to provide information about their event. If you look into the CreateEvent class, you’ll see that these fields are stored in an object called “eventData”, which is an instance of an EventNotificationModel. It’s full list of attributes is below (the ones with comments are used in the CreateEvent class):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090078915/tkkK3YRhK.png" alt="Screenshot 2021-08-04 111134.png" /></p>
<p>“eventData” is a crucial data structure that’s used not just in creating an event but updating and deleting events as well. The init_events_service.dart file illustrates a variety of functions that can be used to perform useful actions on these objects.</p>
<p>One important feature of the example app is its EventList class, which doesn’t come directly with the at_events_flutter dependency:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090109685/Git_SeanU.png" alt="Screenshot 2021-08-04 111152.png" /></p>
<p>This seemingly harmless class is actually doing quite a bit of work behind the scenes! EventList listens to an event stream that comes from an EventService object (a class within the at_event_flutter dependency) and displays them in a ListView format. Clicking on one of these events, you’ll be navigated to a popup of a CreateEvent instance with its “isUpdate” parameter set to true. This allows you to edit and save any event of your choice!</p>
<p>So, what events will you create with at_events_flutter?</p>
]]></content:encoded></item><item><title><![CDATA[Using IDE’s Effectively, Some Tips and Tricks for Flutter/Dart Development]]></title><description><![CDATA[The world keeps advancing in terms of technology and new tools have come in place to help ease the transition. One of the advancements has been the introduction of Flutter which uses the programming language Dart. Flutter can be used to develop Apps ...]]></description><link>https://blog.atsign.dev/using-ides-effectively-some-tips-and-tricks-for-flutterdart-development</link><guid isPermaLink="true">https://blog.atsign.dev/using-ides-effectively-some-tips-and-tricks-for-flutterdart-development</guid><category><![CDATA[IDEs]]></category><category><![CDATA[@platform]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[Damien Bahati]]></dc:creator><pubDate>Mon, 09 Aug 2021 18:30:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628533513462/6GNgajXXO.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The world keeps advancing in terms of technology and new tools have come in place to help ease the transition. One of the advancements has been the introduction of Flutter which uses the programming language Dart. Flutter can be used to develop Apps for both IOS and Android at the same time from one codebase. Flutter is also not limited to just mobile applications, but a developer can create web applications as well! In this article we will look at some of the shortcuts and extensions that can be found in VS Code which makes development a lot easier and efficient. Here I interview Tyler and Xavier to get some of the tips and tricks on how to efficiently use the language.</p>
<p>Xavier Chanthavong is studying at Ontario Tech University for a Bachelor of Science in Computer Science. He is an avid learner and seeks to share his findings with the world. When he isn’t writing code or coming up with new ideas, you can find him playing guitar, or video games with his friends. We caught up with him to hear more on his experience using Dart/Flutter IDEs. Be sure to check out his <a target="_blank" href="https://www.linkedin.com/in/xavierchanth/">LinkedIn</a> and <a target="_blank" href="https://github.com/XavierChanth">Github</a>!</p>
<p>Tyler Trott, who is also studying at Ontario Tech University is enrolled in the Bachelor of Science in Computer Science with a specialization in Data Science. Tyler was actually part of the first group of interns ever at <a target="_blank" href="https://atsign.com">Atsign</a> and has found himself as a Developer Advocate paving the way for the new wave of interns and developers! If he doesn’t have his nose buried in his computer, you’ll more than likely find him on the tennis court or the ski slopes! Maybe one day, he’ll figure out how to do both at the same time. Be sure to check out his <a target="_blank" href="https://www.linkedin.com/in/tyler-t-b307b0107/">LinkedIn</a> and <a target="_blank" href="https://github.com/TylerTrott">Github</a>!</p>
<p>I asked the two whether there was an IDE which focuses on development in Dart/Flutter, here are their thoughts:</p>
<p>First, Tyler explains that there is no specific Integrated Development Environment (or IDE) for Dart/Flutter but there are a few general IDE’s that allow for development with the two and the ones that come to mind very quickly are Android Studio, IntelliJ and Visual Studio Code. Xavier adds that those are the ones recommended by Flutter. Hence, the three would be the best options. Tyler adds that Visual Studio Code is his favourite IDE for the long term due to the almost seemingly endless list of programming languages that are supported on the platform, but Xavier mentions that Android Studio is easier to use when you are new to Dart/Flutter due to its more app development based purpose.</p>
<p>We then went on to discuss the basis of Flutter/Dart, just a quick understanding of the two: Xavier goes first to share that Flutter is a very declarative language and is supposed to be treated as reading a function of states, but in some cases what is really powerful is being able to define abstract classes or implement things such object orientated programming into your data model. Because you can have some flexible stuff going on behind the scenes which you don’t have the option to do with something such as React-Native as easily.</p>
<p>Furthermore, he explains that Dart/Flutter is somewhere between C language and Javascript/Typescript which gives a lot of flexibility on how you can treat the language. In addition, the learning curve is low, hence if you know any of these languages, it would be easy to catch up with Dart/Flutter. Tyler adds to the discussion that he comes from a C++ background, and it was easy to get into Dart because of this. Dart can be a ‘boring’ language, meaning it doesn’t have incredibly innovative workings and can tend to feel very complex such as a typical C language, but can be quite hard to get a hang of if you are coming from the aforementioned languages.</p>
<p>Now digging deeper into the Flutter recommended IDE’s:</p>
<p><strong>Android Studio</strong></p>
<p>To begin with Android Studio, it is a wonderful environment to create projects for Android devices whilst simultaneously allowing you to set up an Android Virtual Device (or AVD) to emulate these projects developed for testing purposes. If you are looking for an easy IDE to get started on, this is the one recommended by the @ Company. You’ll find that most of the code, documentation, and tutorials are created with Android Studio at Atsign.</p>
<p><strong>Visual Studio</strong></p>
<p>Visual Studio Code, often referred to as VS Code, when compared to Android Studio is the most customizable. Most of the Atsign developers also use Visual Studio Code. VS Code has many community-driven extensions that allow for ease of life such as Bracket Colorizer, Better Comments and Error LEns, just to name a few.</p>
<p><strong>IntelliJ</strong></p>
<p>IntelliJ is another commonly used IDE, which has a similar feel to Android Studio (as Android Studio is powered by IntelliJ!).</p>
<p>I asked Xavier and Tyler for some of the common Keyboard Shortcuts that can be utilized: In this section, we will focus on the shortcuts used with VS Code, but may also be applicable when using other IDE’s. Time is precious and there are shortcuts that help a lot when coding. You can find the list of default keyboard shortcuts and search for shortcuts by using CTRL+K+S. Below, I’ve listed some of these shortcuts.</p>
<p><strong>Code Assist or Code Completion (Ctrl + Space)</strong> While coding, this is one of the important tools, especially for beginners or if you get stuck somewhere and would like to check for options, you can use Ctrl+Space to get a list of curated code completion options.</p>
<p><strong>Quick Fix or the Magic Wand (Ctrl + . =&gt; Quick Fix)</strong></p>
<p>For faster development in Flutter, you can use the quick fix tool which is Ctrl+.. This helps in fixing the code with some additional requirements without messing with your code or your widget structure and gives you less headache of manually correcting everything.</p>
<p><strong>Extract Widget (Ctrl + . =&gt; Extract Widget)</strong></p>
<p>This can come in handy a lot if you want to split your widgets into smaller widgets. Just click on any widget and then click Ctrl+. and click Extract Widget option and give a name for the new widget and click Enter and that is it.</p>
<p><strong>Stateless Widget to Stateful Widget (Ctrl + . =&gt; Convert to Stateful Widget)</strong></p>
<p>Now let us say, you wanted a Stateful widget, but have already set up a Stateless Widget. Simply select the Stateless Widget you wish to convert and click Ctrl + . and then select the option Convert to Stateful Widget and Vs Code will automatically convert this widget for you. Some Debugging Shortcuts: · F5 =&gt; Start Debugging · CTRL + F5 =&gt; Start Without Debugging · Shift + F5 (macOS: Cmd + Shift + F5) =&gt; Restart Debugging or Hot Reload</p>
<p>In conclusion, IDEs can be a useful tool that can save developers a lot of time and enable them to develop applications swiftly and efficiently. The aforementioned tips and tricks by Tyler and Xavier can guide and give a clear introduction to IDEs.</p>
]]></content:encoded></item><item><title><![CDATA[The at_contacts_flutter library on the atPlatform]]></title><description><![CDATA[Too many atSigns to keep track of? That’s no problem with the at_contacts_flutter package! Simply add the at_contacts_flutter dependency to your project, and you’ll be capable of performing several actions with your atSign contacts.
Be sure to check ...]]></description><link>https://blog.atsign.dev/the-atcontactsflutter-library-on-the-atplatform</link><guid isPermaLink="true">https://blog.atsign.dev/the-atcontactsflutter-library-on-the-atplatform</guid><category><![CDATA[Dart]]></category><category><![CDATA[app development]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[@platform]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Damien Bahati]]></dc:creator><pubDate>Mon, 09 Aug 2021 18:13:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628532301114/fLbvxAPIF.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Too many atSigns to keep track of? That’s no problem with the at_contacts_flutter package! Simply add the at_contacts_flutter dependency to your project, and you’ll be capable of performing several actions with your atSign contacts.</p>
<p>Be sure to check our  <a target="_blank" href="https://pub.dev/packages/at_contacts_flutter">pub.dev</a> to see the most up-to-date version at_contacts_flutter! </p>
<p>In order to implement the at_contacts_flutter widget, you must first, of course, create an AtClientService instance and authenticate an atSign. If you look through the example application dedicated to the at_contacts_flutter widget, you will see on load that it asks you to either authenticate an atSign or use one that is already authenticated on your device. </p>
<p>After you have successfully onboarded an atSign, you will be taken to the page where you may choose to show your contacts or show your blocked contacts. On load of this page, the following code will run, creating an AtClientService instance. The contacts service needs to be initialized with the atClient from the AtClientService, current authenticated atSign, and the root domain.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090494502/gsBuTxAFW.png" alt="Screenshot 2021-08-04 111805.png" />
After successfully initializing the contact service, you will now be capable of getting the list of contacts that exist for the authenticated atSign. This is as easy as simply passing the contacts into a variable. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090528674/9ZIIoh_e-.png" alt="Screenshot 2021-08-04 111824.png" /></p>
<p>This fetchContacts() function exists in the _contactService file, which can be found within the at_contact’s  ‘services’ folder. If you want to do more than just get the list of contacts, you have the capability of adding more contacts to this list, in addition to removing or blocking any. 
In order to retrieve the list of blocked contacts, it is similar to retrieving the regular list of contacts. The code from the example app demonstrates this well.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090573324/I_LDmQfCm.png" alt="Screenshot 2021-08-04 111846.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090597219/Rpnp9Vq5n.png" alt="Screenshot 2021-08-04 111907.png" /></p>
<p>To block a contact, it is as easy as calling the blockUnblock method. If a contact is blocked, it will unblock the contact. If the contact is not blocked, it will block it for you. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090626298/W2HKT9KqI.png" alt="Screenshot 2021-08-04 111924.png" /></p>
<p>After you block a contact, you may wish to  have that contact removed from the list. All you have to do is simply implement the code below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628090649987/1DjIq-mRj.png" alt="Screenshot 2021-08-04 111944.png" />
Along with the previously stated functionalities, the at_contacts_flutter package also provides the UI so there’s no need to set up a separate page to house these functions!</p>
]]></content:encoded></item><item><title><![CDATA[Updating Metadata on the atPlatform]]></title><description><![CDATA[atProtocol exposes various attributes that can be set as the metadata for a key.
Please refer to the AtMetadata class for the complete list of metadata attributes.
The focus of this article is on how to update the metadata of a key.
Description:
The ...]]></description><link>https://blog.atsign.dev/updating-metadata-on-the-atplatform</link><guid isPermaLink="true">https://blog.atsign.dev/updating-metadata-on-the-atplatform</guid><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Sitaram Kalluri]]></dc:creator><pubDate>Tue, 03 Aug 2021 17:03:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628010147234/Xtd2eqAkG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>atProtocol exposes various attributes that can be set as the metadata for a key.</p>
<p>Please refer to the <a target="_blank" href="https://github.com/atsign-foundation/at_server/blob/master/at_secondary/at_persistence_secondary_server/lib/src/model/at_meta_data.dart">AtMetadata</a> class for the complete list of metadata attributes.</p>
<p>The focus of this article is on how to update the metadata of a key.</p>
<p>Description:</p>
<p>The update:meta verb is used for updating the metadata of a key. If a key is already present, the metadata is updated. If a key is not present, a new key is created with the given metadata with a ‘null’ value. Users can add the value using the update verb.</p>
<p>Please refer to <a target="_blank" href="https://github.com/atsign-foundation/at_server/blob/master/at_secondary/at_secondary_server/lib/src/verb/handler/update_meta_verb_handler.dart">update_meta_verb_handler</a> for implementation.</p>
<p>SDK offers the ‘ putMeta’ method to update the metadata of a key.</p>
<p>Updating metadata of a key using the SDK.</p>
<pre><code class="lang-plaintext">var currentAtSign = '@alice';

//Creating AtClient SDK instance.
await AtClientImpl.createClient(
   currentAtSign, 'me', TestUtil.getAlicePreference());

// Getting atClient Instance
var atClient = await AtClientImpl.getClient(currentAtSign);

var metadata = Metadata()..ttl = 600000
                         ..ttb = 600000;
var atKey = AtKey()..phone
                   ..sharedWith = ‘@bob’
                   ..sharedBy=’@alice’
                   ..metadata=metadata;
var result  = await atClient.putMeta(atKey);
</code></pre>
<p>The AtClient exposes the ‘getMeta’ which can be used to get the metadata of the key. The ‘getMeta’ method returns metadata in a JSON String.</p>
<pre><code class="lang-plaintext">var currentAtSign = '@alice';

//Creating AtClient SDK instance.
await AtClientImpl.createClient(
   currentAtSign, 'me', TestUtil.getAlicePreference());

// Getting atClient Instance
var atClient = await AtClientImpl.getClient(currentAtSign);

var atKey = AtKey()
 ..key = 'phone'
 ..sharedWith = '@bob
 ..sharedBy = '@alice';
var metadata = await atClient.getMeta(atKey);
</code></pre>
<p>Response :</p>
<pre><code class="lang-plaintext">Metadata{ttl: 600000, ttb: 600000, ttr: null,ccd: null, isPublic: false, isHidden: false, availableAt : 2021-02-18 02:40:03.207Z, expiresAt : 2021-02-18 02:50:03.207Z, refreshAt : null, createdAt : 2021-02-18 02:30:03.207Z,updatedAt : 2021-02-18 02:45:03.207Z,isBinary : null, isEncrypted : null, isCached : false, dataSignature: null}
</code></pre>
<p>For more samples, please refer to update_meta_verb_test.</p>
<p>We are working to make the internet better. Please come code with us!!!</p>
]]></content:encoded></item><item><title><![CDATA[Monitoring Notifications With Regular Expressions]]></title><description><![CDATA[Introduction:
The monitor verb in the @protocol is used to stream the received notifications.
Please check the  MonitorVerbHandler  source code to learn more about the implementation of the monitor verb.
The article focuses on the usage of the monito...]]></description><link>https://blog.atsign.dev/monitoring-notifications-with-regular-expressions</link><guid isPermaLink="true">https://blog.atsign.dev/monitoring-notifications-with-regular-expressions</guid><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Regex]]></category><dc:creator><![CDATA[Sitaram Kalluri]]></dc:creator><pubDate>Tue, 03 Aug 2021 17:01:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615528319596/7ekR3_TlY.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Introduction:</p>
<p>The monitor verb in the @protocol is used to stream the received notifications.</p>
<p>Please check the  <a target="_blank" href="https://github.com/atsign-foundation/at_server/blob/master/at_secondary/at_secondary_server/lib/src/verb/handler/monitor_verb_handler.dart">MonitorVerbHandler</a>  source code to learn more about the implementation of the monitor verb.</p>
<p>The article focuses on the usage of the monitor verb and how to filter notifications using regular expression(regex).</p>
<p>Description:</p>
<p>The monitor verb is used to stream the notification received from the other atsign user. Optionally a regular expression can suffix the monitor verb to filter the notifications. When regex is provided, only the notifications that match the regex are returned.</p>
<p>Example:</p>
<p>@alice notifying @bob</p>
<pre><code><span class="hljs-attribute">Var</span> currentAtSign = <span class="hljs-string">'<span class="hljs-variable">@alice</span>'</span>;

<span class="hljs-attribute">var</span> atKey = AtKey()
 ..key = <span class="hljs-string">'phone'</span>
 ..sharedWith = <span class="hljs-string">'<span class="hljs-variable">@bob</span>’
 ..sharedBy = currentAtSign;

var value = '</span>+<span class="hljs-number">1</span> <span class="hljs-number">100</span> <span class="hljs-number">200</span> <span class="hljs-number">345</span><span class="hljs-string">';

var result = await atClient.notify(atKey, value, OperationEnum.update, notifier: '</span>system<span class="hljs-string">');</span>
</code></pre><p>Monitor Verb running on @bob</p>
<pre><code><span class="hljs-attribute">await</span> AtClientImpl.createClient(
   <span class="hljs-string">'<span class="hljs-variable">@bob</span>'</span>, <span class="hljs-string">'<span class="hljs-variable">@persona</span>'</span>, preference);
<span class="hljs-attribute">var</span> atClient = await AtClientImpl.getClient(<span class="hljs-string">'<span class="hljs-variable">@bob</span>’);
await atClient.startMonitor(preference.privateKey, _notificationCallBack);</span>
</code></pre><p>Response</p>
<pre><code>notification: {<span class="hljs-string">"id"</span>:<span class="hljs-string">"c6fab088-9e01-4374-a1da-182589eb8c7f"</span>,<span class="hljs-string">"from"</span>:<span class="hljs-string">"@alice"</span>,<span class="hljs-string">"to"</span>:<span class="hljs-string">"@bob"</span>,<span class="hljs-string">"key"</span>:<span class="hljs-string">"@bob:phone@alice"</span>,<span class="hljs-string">"value"</span>:null,<span class="hljs-string">"operation"</span>:<span class="hljs-string">"update"</span>,<span class="hljs-string">"epochMillis"</span>:<span class="hljs-number">1613889831624</span>}

notification: {<span class="hljs-string">"id"</span>:<span class="hljs-string">"c6fab088-9e01-4374-a1da-182589eb8c7f"</span>,<span class="hljs-string">"from"</span>:<span class="hljs-string">"@alice"</span>,<span class="hljs-string">"to"</span>:<span class="hljs-string">"@bob"</span>,<span class="hljs-string">"key"</span>:<span class="hljs-string">"@bob:address@alice"</span>,<span class="hljs-string">"value"</span>:null,<span class="hljs-string">"operation"</span>:<span class="hljs-string">"update"</span>,<span class="hljs-string">"epochMillis"</span>:<span class="hljs-number">1613889831876</span>}
</code></pre><p>Monitor Verb running on @bob with regex.</p>
<pre><code><span class="hljs-attribute">await</span> AtClientImpl.createClient(
   <span class="hljs-string">'<span class="hljs-variable">@bob</span>'</span>, <span class="hljs-string">'<span class="hljs-variable">@persona</span>'</span>, preference);
<span class="hljs-attribute">var</span> atClient = await AtClientImpl.getClient(<span class="hljs-string">'<span class="hljs-variable">@bob</span>’);
await atClient.startMonitor(privateKey, acceptStream,regex: '</span>phone<span class="hljs-string">'
);</span>
</code></pre><p>Response</p>
<pre><code>notification: {<span class="hljs-string">"id"</span>:<span class="hljs-string">"c6fab088-9e01-4374-a1da-182589eb8c7f"</span>,<span class="hljs-string">"from"</span>:<span class="hljs-string">"@alice"</span>,<span class="hljs-string">"to"</span>:<span class="hljs-string">"@bob"</span>,<span class="hljs-string">"key"</span>:<span class="hljs-string">"@bob:phone@alice"</span>,<span class="hljs-string">"value"</span>:null,<span class="hljs-string">"operation"</span>:<span class="hljs-string">"update"</span>,<span class="hljs-string">"epochMillis"</span>:<span class="hljs-number">1613889831624</span>}
</code></pre><p>We are working to make the Internet better. Please come code with us!</p>
]]></content:encoded></item><item><title><![CDATA[Running Dart on Arm Servers]]></title><description><![CDATA[Last week I wrote about easier automation using GitHub Actions to build multi architecture Docker images. That led me to another question - how to create the binaries to go into those builds? If I'm making an Arm image, then I need an Arm binary to g...]]></description><link>https://blog.atsign.dev/running-dart-on-arm-servers</link><guid isPermaLink="true">https://blog.atsign.dev/running-dart-on-arm-servers</guid><category><![CDATA[Docker]]></category><category><![CDATA[Dart]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[docker images]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Tue, 03 Aug 2021 16:59:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628009964011/rtPwbdjyQm.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week I wrote about <a target="_blank" href="https://blog.atsign.dev/easier-automation-cknyq4eol0he4g1s193au1766">easier automation</a> using GitHub Actions to build multi architecture Docker images. That led me to another question - how to create the binaries to go into those builds? If I'm making an Arm image, then I need an Arm binary to go into that image.</p>
<p>At the moment <a target="_blank" href="https://dart.dev/tools/dart-compile">dart compile</a> only compiles self-contained executable files for the architecture it's being run on. This is a shame, as the underlying tooling is perfectly capable of doing cross compilation as J-P Nurmi explains in <a target="_blank" href="https://medium.com/flutter-community/cross-compiling-dart-apps-f88e69824639">Cross-compiling Dart apps</a>.</p>
<p>Without cross compilation the only alternative is to run <code>dart compile</code> from the Dart SDK on each of the target platforms. Google handily packages up the SDK into a Docker image <a target="_blank" href="https://hub.docker.com/r/google/dart">google/dart</a> but unfortunately that's only available for x86_64 (aka amd64). The <a target="_blank" href="https://github.com/dart-lang/dart_docker/blob/master/base/Dockerfile.template">Dockerfile</a> for that image also makes use of the SDK apt package, but that too is only available for x86_64.</p>
<p>So to run <code>dart compile</code> on multiple architectures, I first needed a multi arch build image:</p>
<pre><code class="lang-dockerfile">FROM debian:stable

ARG DART_VERSION="2.12.4"

WORKDIR /tmp/

RUN \
  apt-get -q update &amp;&amp; apt-get install --no-install-recommends -y -q \
    gnupg2 curl git ca-certificates unzip openssh-client &amp;&amp; \
  case "$(uname -m)" in armv7l | armv7) ARCH="arm";; aarch64) ARCH="arm64";; *) ARCH="x64";; esac &amp;&amp; \
  curl -O https://storage.googleapis.com/dart-archive/channels/stable/release/$DART_VERSION/sdk/dartsdk-linux-$ARCH-release.zip &amp;&amp; \
  unzip dartsdk-linux-$ARCH-release.zip -d /usr/lib/ &amp;&amp; \
  rm dartsdk-linux-$ARCH-release.zip &amp;&amp; \
  mv /usr/lib/dart-sdk /usr/lib/dart

ENV DART_SDK /usr/lib/dart
ENV PATH $DART_SDK/bin:/root/.pub-cache/bin:$PATH
</code></pre>
<p>This is installing some key dependencies (the same as google/dart) and then downloading and installing the Dart SDK according to the output from <code>uname -m</code> to establish the underlying architecture.</p>
<p>With the build image it's then possible to create a lightweight run image based on the work by Google engineer Tony Pujals with his <a target="_blank" href="https://github.com/subfuzion/dart-docker-slim">subfuzion/dart-docker-slim</a>:</p>
<pre><code class="lang-dockerfile"># Originally based on subfuzion/dart-docker-slim
# Using our buildimage as it supports x64|arm|arm64
FROM atsigncompany/buildimage AS dart

# Do all the copying to an output dir here first
# as the buildimage has a shell that can be used
# for multi-arch conditionals
RUN \
  mkdir -p /output/lib &amp;&amp; mkdir -p /output/etc &amp;&amp; \
  mkdir -p /output/etc/ssl/certs &amp;&amp; \
  mkdir -p /output/usr/share/ca-certificates &amp;&amp; \
  case "$(uname -m)" in \
   armv7l | armv7) ARCH="arm-linux-gnueabihf" ; \
    mkdir -p /output/lib/$ARCH ; \
    cp /lib/ld-linux-armhf.so.3 /output/lib/ld-linux-armhf.so.3 ; \
    cp /lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 \
       /output/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 ;; \
   aarch64) ARCH="aarch64-linux-gnu" ; \
    mkdir -p /output/lib/$ARCH ; \
    cp /lib/ld-linux-aarch64.so.1 /output/lib/ld-linux-aarch64.so.1 ; \
    cp /lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 \
       /output/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 ;; \
   *) ARCH="x86_64-linux-gnu" ; \
    mkdir -p /output/lib/$ARCH ; mkdir -p /output/lib64/ ; \
    cp /lib64/ld-linux-x86-64.so.2 /output/lib64/ld-linux-x86-64.so.2 ;; \
  esac &amp;&amp; \
  cp /lib/$ARCH/libdl.so.2 /output/lib/$ARCH/libdl.so.2 &amp;&amp; \
  cp /lib/$ARCH/libc.so.6 /output/lib/$ARCH/libc.so.6 &amp;&amp; \
  cp /lib/$ARCH/libm.so.6 /output/lib/$ARCH/libm.so.6 &amp;&amp; \
  cp /lib/$ARCH/librt.so.1 /output/lib/$ARCH/librt.so.1 &amp;&amp; \
  cp /lib/$ARCH/libpthread.so.0 /output/lib/$ARCH/libpthread.so.0 &amp;&amp; \
  cp /lib/$ARCH/libnss_dns.so.2 /output/lib/$ARCH/libnss_dns.so.2 &amp;&amp; \
  cp /lib/$ARCH/libresolv.so.2 /output/lib/$ARCH/libresolv.so.2 &amp;&amp; \
  cp /etc/nsswitch.conf /output/etc/nsswitch.conf &amp;&amp; \
  cp /etc/resolv.conf /output/etc/resolv.conf &amp;&amp; \
  cp -R /usr/share/ca-certificates /output/usr/share/ca-certificates &amp;&amp; \
  cp -R /etc/ssl/certs /output/etc/ssl/certs

FROM scratch
COPY --from=dart /output /
# Is this even really needed, as Docker uses host hosts?
COPY ./at-runimage/etc-hosts /etc/hosts
</code></pre>
<p>Once again this is using the output of <code>uname -m</code> to determine the correct dependencies to copy in from the build image.</p>
<p>The build image and run image can then be brought together with a Dart application. I put together a trivial one <code>dartshowplatform</code> that prints out version and platform information like:</p>
<pre><code class="lang-logs">$ sudo docker run -it  atsigncompany/dartshowplatform
2.12.4 (stable) (Thu Apr 15 12:26:53 2021 +0200) on "linux_arm64"
</code></pre>
<p>Here's the Dockerfile:</p>
<pre><code class="lang-dockerfile">FROM atsigncompany/buildimage
WORKDIR /app
COPY ./dartshowplatform/showplatform.dart .
RUN dart compile exe /app/showplatform.dart -o /app/dartshowplatform

FROM atsigncompany/runimage
COPY --from=0 /app/dartshowplatform /app/dartshowplatform
ENTRYPOINT ["/app/dartshowplatform"]
</code></pre>
<p>The build image is used to run <code>dart compile</code> and then the binary created from that is copied into the run image.</p>
<p>The full source etc. for the stuff above is available on GitHub <a target="_blank" href="https://github.com/atsign-company/at_dockerfiles">atsign-company/at_dockerfiles</a>.</p>
<p><img src="https://pbs.twimg.com/media/Ez_cg5GWQAEqa0-?format=jpg&amp;name=large" alt="Arm adoption at AWS" /></p>
<p>If you're wondering why we're doing this, it's because people are asking us to support Arm and Arm64, as there's huge growth in the Arm footprint as illustrated above. We can expect it won't be long before we're having to add <a target="_blank" href="https://en.wikipedia.org/wiki/RISC-V">RISC-V</a> support too, as that Instruction Set Architecture (ISA) gains traction and wider adoption. The days of WinTel hegemony are over, which means dealing with a lot more heterogeneity, and that demands greater automation to avoid being drowned by the complexity of it all.</p>
]]></content:encoded></item><item><title><![CDATA[Steps before forking]]></title><description><![CDATA[Dart introduced Sound null safety with version 2.12, so we've recently been very busy making sure all our stuff supports it. That also means ensuring that all of our underlying dependencies are ready for Null safety too. In most cases we're using pop...]]></description><link>https://blog.atsign.dev/steps-before-forking</link><guid isPermaLink="true">https://blog.atsign.dev/steps-before-forking</guid><category><![CDATA[Dart]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Fri, 09 Jul 2021 16:02:41 GMT</pubDate><content:encoded><![CDATA[<p>Dart introduced <a target="_blank" href="https://dart.dev/null-safety">Sound null safety</a> with version 2.12, so we've recently been very busy making sure all our stuff supports it. That also means ensuring that all of our underlying dependencies are ready for <code>Null safety</code> too. In most cases we're using popular libraries that are actively maintained, so they've already made the shift. But there are a few things we use that haven't.</p>
<p>So we should just fork the code, make the fix, and move on with our version?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1625846313017/LgvbjrGvA.jpeg" alt="2330044065_03682e251a_w.jpg" /></p>
<blockquote>
<p><a target="_blank" href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a> image by <a target="_blank" href="https://flic.kr/p/4xU5Vx">i_yudai</a></p>
</blockquote>
<p>Not so fast... forking without even trying to work with the upstream developer is just rude. So we've established a three-step process:</p>
<h2 id="heading-1-raise-an-issue-on-the-upstream-repo">1. Raise an issue on the upstream repo</h2>
<p>This lets the maintainer know that there's demand for a change. They may already have plans to do it, and an issue might be the nudge that they need. Or perhaps they're swamped with other work, in which case they can hang out the <code>help needed</code> label.</p>
<p>The best case here is that the upstream maintainer quickly responds with their own change to the code, and publishes it to <a target="_blank" href="https://pub.dev">pub.dev</a>.</p>
<p>Worst case, there's no response at all, so after allowing a little while for a response, it's time to move on to:</p>
<h2 id="heading-2-submit-a-pull-request-to-the-upstream-repo-with-the-desired-changes">2. Submit a pull request to the upstream repo with the desired changes</h2>
<p>This will of course close the issue raised at #1, and now we're asking the maintainer to review and merge rather than start from scratch, so most of the work has been done for them.</p>
<p>The best case here is that the pull request is quickly merged, and the resulting package is updated on [pub.dev](https://pub.dev).</p>
<p>Again the worst case is no response at all, so after waiting another little while, it's time to move on to:</p>
<h2 id="heading-3-fork-the-repo-and-publish-a-variant">3. Fork the repo and publish a variant</h2>
<p>The work on the code is already done from #2, so all that's needed is to come up with a variant name, clean up some metadata, and publish.</p>
<p>But now we have another dependency that we're maintaining rather than just contributing to, which isn't ideal.</p>
]]></content:encoded></item><item><title><![CDATA[Advanced Customization in atWavi]]></title><description><![CDATA[The Wavi app launched by Atsign is a public space for your business, your projects, or yourself. Provide as little or as much information as you want on your weblet (basically a microsite), which will be made available to the public on our directory....]]></description><link>https://blog.atsign.dev/advanced-customization-in-atwavi</link><guid isPermaLink="true">https://blog.atsign.dev/advanced-customization-in-atwavi</guid><category><![CDATA[@platform]]></category><dc:creator><![CDATA[Tinashe Seremwe]]></dc:creator><pubDate>Fri, 09 Jul 2021 02:52:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1625796654585/mxzvPI35V.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <a target="_blank" href="https://wavi.ng">Wavi</a> app launched by <a target="_blank" href="https://atsign.com">Atsign</a> is a public space for your business, your projects, or yourself. Provide as little or as much information as you want on your weblet (basically a microsite), which will be made available to the public on our directory. You can opt to publish contact information, location, media, social media links, even gamer tags. For more advanced customization, HTML and CSS can be used to format custom content.</p>
<p>In order to do this, start by selecting the <strong>Text</strong> field type, under <strong>Custom Content</strong>. For the examples shown in this article, you can copy the code and replace the text in the asterisks with your own content.</p>
<h2 id="heading-html-tags">HTML Tags</h2>
<p>HTML Tags are like keywords that determine how a webpage should be displayed on screen. Most html tags come in two parts.</p>
<ul>
<li><p>An opening tag is used to mark the beginning of a section of page content. eg <code>&lt;html&gt;</code></p>
</li>
<li><p>A closing tag is used to make the end of a section of page content. eg <code>&lt;/html&gt;</code></p>
</li>
</ul>
<p>Most tag types require the use of both opening and closing tags. However some do not, such as <code>&lt;br&gt;</code>. We will go over some of the tags that you might find useful when customizing your wavi weblet.</p>
<h2 id="heading-positioning">Positioning</h2>
<p>To center your content like how this article is centered, the <code>&lt;center&gt;</code> tags can be used. Anything that is in between the tags will appear centered on the screen.</p>
<p><strong>Code</strong><br /><code>&lt;center&gt; *your content here*&lt;/center&gt;</code></p>
<h2 id="heading-paragraphs-and-line-breaks">Paragraphs and line breaks</h2>
<p>There are two ways to create paragraphs and line breaks. The first way is by using <code>&lt;p&gt;</code> tags. When you enter a <code>&lt;p&gt;</code> tag, it will be handled as the beginning of a new paragraph.</p>
<p><strong>Code</strong><br /><code>&lt;p&gt; *your content here* &lt;/p&gt;</code></p>
<p>The second way is by using the <code>&lt;br&gt;</code> tag, which creates a line break in your content. You do not need to close this tag. A new line will be started on the content that follows the <code>&lt;br&gt;</code>.</p>
<h2 id="heading-colored-text">Colored Text</h2>
<p>If you want to add a pop of color to the text on your weblet, you can use CSS eg <code>style="color:#00b7b8;"</code> and <code>&lt;span&gt;</code> tags if you are only coloring specific parts of your paragraph.</p>
<p><strong>Code</strong><br /><code>&lt;p&gt; &lt;span style="color:#00b7b8;"&gt; This text is teal colored &lt;/span&gt;&lt;/p&gt;</code></p>
<h2 id="heading-scrolling-text">Scrolling Text</h2>
<p>To display a scrolling area of text, <code>&lt;marquee&gt;</code> tags can be used. These produce text that will scroll from one end of the text area to the other, indefinitely.</p>
<p><strong>Code</strong><br /><code>&lt;marquee&gt; *your text here* &lt;/marquee&gt;</code></p>
<p>This will display a single line of scrolling text.</p>
<h2 id="heading-hyperlinks">Hyperlinks</h2>
<p>To insert a link that has alternative text displayed on the screen, you can use <code>&lt;a&gt;</code> tags. The text in between the <code>&lt;a&gt;</code> and <code>&lt;/a&gt;</code> tag is what will be displayed on screen.</p>
<p><strong>Code</strong><br /><code>&lt;a target="_blank" href="*your link here*"&gt;*your text here*&lt;/a&gt;</code></p>
<p><strong>Explanation</strong>  </p>
<p>target="_blank" will open the link in a new tab.</p>
<p>href= specifies the URL of the page the link points to.</p>
<h2 id="heading-images">Images</h2>
<p>Online images can be added to your wavi weblet using the <code>&lt;img&gt;</code> tag. The image must be accessible via URL. The source could be a website, a google image search or an image that you have uploaded onto an image hosting site like imgbb.com.</p>
<p><strong>Code</strong><br /><code>&lt;img width="100%" src="*your image url here*"&gt;</code></p>
<p><strong>Explanation</strong><br />width="100%" specifies the width of the image in relation to the screen. This can also be set in pixels eg "400px".</p>
<p>src="" specifies the URL for the image that will be displayed.</p>
<h2 id="heading-iframes">iFrames</h2>
<p>An iFrame is a framed section in a web page that displays content from another web page. In the wavi app, you can use iFrames to display any non text content. This includes gifs, videos, images, audio players and even games.</p>
<p>The <code>&lt;iframe&gt;</code> tags are usually generated by websites when you select the option to share the website’s content via embedding. The general structure of the <code>&lt;iframe&gt;</code> tags is as follows.</p>
<p><strong>Code</strong>  </p>
<pre><code class="lang-plaintext">&lt;/iframe&gt;```

**Explanation** &lt;br&gt;
src="" specifies the URL for the content that will be embedded into the iFrame.
width="" specifies the width of the iFrame
height="" specifies the height of the iFrame


Other attributes are usually added to the iFrame such as width and height specifiers. 

## Videos
&lt;iframe width="100%" height="400" src="https://www.youtube.com/embed/NHDDQbsRHU8" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" enablejsapi="true" id="widget2"&gt;&lt;/iframe&gt; 

In order to get the embed code for a video on Youtube, Vimeo or any other streaming service, you will need to be on a desktop. While on the chosen video, click share and then select embed in order to access the embed code. You can then transfer this code to your mobile device via email or other means.

Alternatively, you can use the sample iframe code provided below. Navigate to your chosen video and copy the video URL. If the URL is https://youtube.com/NHDDQbsRHU8 you only need the last part of the URL after the last / so in this case it would be just 'NHDDQbsRHU8'. Copy this and paste it into the iframe code after embed/ as shown in the example.

**Code** &lt;br&gt;
```&lt;iframe width="100%" height="400" src="https://www.youtube.com/embed/*your video code here*" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" enablejsapi="true" id="widget2"&gt;&lt;/iframe&gt; ```

## Music Players
&lt;iframe src="https://open.spotify.com/embed/playlist/1vDhN0uiRxJiBR8JmZpnLl" width="100%" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;

Playable music can be added from Spotify, Soundcloud and other streaming services. In order to get the embed code for a specific song or playlist, select share and then choose embed to access the embed code. Refer to the code below to gain an understanding of what the embed code may look like.

**Code** &lt;br&gt;
```&lt;iframe src="*your playlist link here*" width="100%" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;```

Once embedded, a music player will be displayed on the weblet. 

Depending on the streaming service, people may be required to login to their streaming accounts in order to play the music on your weblet.

## Gifs
&lt;iframe src="https://giphy.com/embed/MeCyHmilLHIjWPcpWV" width="100%" height="480" frameBorder="0" class="giphy-embed" allowFullScreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a href="https://giphy.com/gifs/tea-trex-loof-and-timmy-MeCyHmilLHIjWPcpWV"&gt;via GIPHY&lt;/a&gt;&lt;/p&gt;

In order to add a gif to your weblet, you will need to find a gif online on a gif website like https://giphy.com/ . Once you have selected a gif, click on share or embed in order to access the embed code. You can then transfer this code to your mobile device via email or other means. The code will look something like this. 

**Code** &lt;br&gt;
```&lt;iframe src="https://giphy.com/embed/MeCyHmilLHIjWPcpWV" width="100%" height="480" frameBorder="0" class="giphy-embed" allowFullScreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a href="https://giphy.com/gifs/tea-trex-loof-and-timmy-MeCyHmilLHIjWPcpWV"&gt;via GIPHY&lt;/a&gt;&lt;/p&gt;```. 

To remove the hyperlink that says via GIPHY beneath the gif, delete the ```&lt;p&gt;``` tags and everything in between them. 

Anything with an embed code can be added to your weblet using iFrames. 

Some examples include:
- Calendars
- Countdown timers
- Google docs, sheets and slides
- Code compilers and snippets
- Maps

The list is almost endless.

For inspiration, have a look at the sample weblets shown on [https://wavi.ng/@wavi](https://wavi.ng/@wavi).
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Planning Poker, without Copy/Paste]]></title><description><![CDATA[We use Planning Poker as a fun way to gauge the effort needed for various pieces of work from our backlog. The tool offers integration with Jira, but we use GitHub projects, and that's left us having to copy and paste card details from one place to a...]]></description><link>https://blog.atsign.dev/planning-poker-without-copypaste</link><guid isPermaLink="true">https://blog.atsign.dev/planning-poker-without-copypaste</guid><category><![CDATA[GitHub]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[agile]]></category><category><![CDATA[planning]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Wed, 02 Jun 2021 15:36:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1622652971819/VWBuiAmAo.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We use <a target="_blank" href="https://www.planningpoker.com/">Planning Poker</a> as a fun way to gauge the effort needed for various pieces of work from our backlog. The tool offers integration with Jira, but we use GitHub projects, and that's left us having to copy and paste card details from one place to another.</p>
<p>Thankfully planningpoker.com also offers .csv import, and it's reasonably easy to generate a suitable file using some <a target="_blank" href="https://github.com/atsign-company/dump_cards">scripts that harness the GitHub API</a>.</p>
<p>First we use <code>listprojects.py</code> to get the right project ID:</p>
<pre><code><span class="hljs-number">6018453</span> <span class="hljs-number">3</span> <span class="hljs-string">Used</span> <span class="hljs-string">for</span> <span class="hljs-string">tracking</span> <span class="hljs-string">backlogs,</span> <span class="hljs-string">bugs,</span> <span class="hljs-string">issues,</span> <span class="hljs-string">enhancements</span> <span class="hljs-string">across</span> <span class="hljs-string">multiple</span> <span class="hljs-string">repo's</span> <span class="hljs-string">in</span> <span class="hljs-string">Open</span> <span class="hljs-string">Source.</span>
<span class="hljs-number">12437918</span> <span class="hljs-number">5</span> <span class="hljs-string">Open</span> <span class="hljs-string">source</span> <span class="hljs-string">development</span> <span class="hljs-string">and</span> <span class="hljs-string">sprint</span> <span class="hljs-string">planning</span> <span class="hljs-string">for</span> <span class="hljs-string">https://atsign.dev.</span>
</code></pre><p>Then <code>list_project_columns.py</code> to get the column name for the sprint we're planning:</p>
<pre><code><span class="hljs-number">11918750</span> <span class="hljs-string">Backlog</span>
<span class="hljs-number">12092381</span> <span class="hljs-string">Bugs</span>
<span class="hljs-number">14337222</span> <span class="hljs-string">PR12</span> <span class="hljs-bullet">-</span> <span class="hljs-number">107</span> <span class="hljs-string">SP's</span> <span class="hljs-string">(Capacity</span> <span class="hljs-number">105</span><span class="hljs-string">)</span>
<span class="hljs-number">13742763</span> <span class="hljs-string">Spike</span> <span class="hljs-bullet">-</span> <span class="hljs-string">POC</span>
</code></pre><p>and lastly <code>dump_project_cards.py</code> to generate a .csv file that will look something like this:</p>
<pre><code><span class="hljs-string">Issue</span> <span class="hljs-string">Key,Summary,Description,Acceptance</span> <span class="hljs-string">Criteria,Story</span> <span class="hljs-string">Points</span>
<span class="hljs-number">38</span><span class="hljs-string">,Integrate</span> <span class="hljs-string">Rocky's</span> <span class="hljs-string">design</span> <span class="hljs-string">into</span> <span class="hljs-string">Docsy</span> <span class="hljs-string">WTF</span> <span class="hljs-string">site,,,</span>
<span class="hljs-number">43</span><span class="hljs-string">,Home/landing</span> <span class="hljs-string">page</span> <span class="hljs-string">messaging</span> <span class="hljs-string">and</span> <span class="hljs-string">content,,,</span>
<span class="hljs-number">40</span><span class="hljs-string">,Docs</span> <span class="hljs-string">Page</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Left</span> <span class="hljs-string">hand</span> <span class="hljs-string">menu</span> <span class="hljs-string">items</span> <span class="hljs-string">and</span> <span class="hljs-string">flow,,,</span>
<span class="hljs-number">41</span><span class="hljs-string">,Landing</span> <span class="hljs-string">page</span> <span class="hljs-string">design</span> <span class="hljs-string">for</span> <span class="hljs-string">each</span> <span class="hljs-string">of</span> <span class="hljs-string">main</span> <span class="hljs-string">category</span> <span class="hljs-string">pages</span> <span class="hljs-string">in</span> <span class="hljs-string">the</span> <span class="hljs-string">top</span> <span class="hljs-string">menu,,,</span>
<span class="hljs-number">46</span><span class="hljs-string">,Open</span> <span class="hljs-string">Source</span> <span class="hljs-string">Policies</span> <span class="hljs-string">&amp;</span> <span class="hljs-string">Process,,,</span>
<span class="hljs-number">45</span><span class="hljs-string">,Update</span> <span class="hljs-string">Nomenclature</span> <span class="hljs-string">on</span> <span class="hljs-string">Pub.dev</span> <span class="hljs-string">and</span> <span class="hljs-string">other</span> <span class="hljs-string">pages,,,</span>
</code></pre><p>The .csv file can then be imported into planningpoker.com, and all that tedious coping and pasting (and errors and omissions that might go with it) can be avoided.</p>
]]></content:encoded></item><item><title><![CDATA[Part 1- The Notify Verb]]></title><description><![CDATA[Overview:
The notify verb is a powerful method to send an encrypted data transmission to an @ sign’s secondary server, in this part of, “The Power Behind Sharing Data on the atPlatform”, we’ll be deep diving into just how the notify verb works and ho...]]></description><link>https://blog.atsign.dev/part-1-the-notify-verb</link><guid isPermaLink="true">https://blog.atsign.dev/part-1-the-notify-verb</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Flutter Examples]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Android]]></category><category><![CDATA[@platform]]></category><dc:creator><![CDATA[Tyler Trott]]></dc:creator><pubDate>Mon, 03 May 2021 22:56:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1620083313588/tNAzn3Cy0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-overview">Overview:</h3>
<p>The notify verb is a powerful method to send an encrypted data transmission to an @ sign’s secondary server, in this part of, “The Power Behind Sharing Data on the atPlatform”, we’ll be deep diving into just how the notify verb works and how to implement it into your own Flutter application.</p>
<h3 id="heading-what-is-the-notify-verb">What is the Notify Verb?</h3>
<p>The notify verb is used to send data from one atSign’s secondary server to another atSign’s secondary server securely. It is a back-end developed verb that already exists so you as the developer do not have to worry about building it yourself! What this means is that all you have to do is choose what information you’d like to send to another atSign’s secondary server, pass this information through the parameters of the verb, and the atPlatform will take care of the messy parts, such as encrypting, transmitting and other headaches!</p>
<h3 id="heading-the-arguments-of-the-notify-verb">The Arguments of the Notify Verb</h3>
<p>The verb itself can be seen with three main parameters; AtKey atKey, String value, and OperationEnum operation. The AtKey atKey is an object that contains the following member variables (or fields):</p>
<pre><code class="lang-plaintext">class AtKey {
// This is where the at_key itself will be stored
String key; 
// The @sign in which the information is
//  being passed to (can even be from yourself)
String sharedWith;
// The @sign in which the information
// is being passed from
String sharedBy;
// The namespace being the name of the 
// application where the information sent originates
String namespace;
// The metadata of the AtKey itself described 
// in another article
Metadata metadata;
// This boolean variable has not actually been 
// implemented from our backend just yet so stick around for updates!
bool isRef = false;
</code></pre>
<p>The String variable named value is actually the AtKey object being converged as a string, more on this later. Finally, we have our OperationEnum operation which is a very important part of the notify verb as this is the deciding factor of what type of notification will be sent to the chosen secondary server. You will see that the OperationEnum is defined as an enum (which is, in its simplest terms, a nice collection of constants) and in our case, the constants are; update, delete, append, and remove. What do these all mean, however?</p>
<p>When OperationEnum.update is passed through notify, the atSign who is receiving the notification will cache the value sent in either an already pre-existing version of the key and update the value with the new value sent, or create an entirely new key to store the value in. When OperationEnum.delete is passed through notify, the secondary server that has received the notification will then begin the process of deleting the cached key that was previously sent to them. Both the append and remove attributes have not yet been implemented by our backend team members, however, their functionality is still quite self-explanatory. OperationEnum.append will simply append the value to an already cached key and OperationEnum.remove will remove the specified value from an already cached key. Continue staying up-to-date with the atPlatform to see when append and remove have been implemented!</p>
<p>Now that all of the mandatory arguments have been discussed, when an atPlatform developer takes a closer look, they will notice that the notify verb also contains five other optional arguments! These arguments are; MessageTypeEnum messageType, PriorityEnum priority, StrategyEnum strategy, int latestN, and String notifier = SYSTEM. I won’t go into deep detail as to what these all are as some of them are quite complex, especially for a beginner developer. Regardless of the complexity of these arguments, we still endorse the brave-hearted to play around with them!</p>
<h3 id="heading-implementing-the-notify-verb-in-your-application">Implementing the Notify Verb in your Application</h3>
<p>Now that you have a foundational understanding of what the notify verb is and what it is doing, it is time to start using it yourself! The notify verb is succinctly utilized in Atsign’s <a target="_blank" href="https://github.com/atsign-foundation/at_demos/tree/master/at_cookbook">at_cookbook</a> demo app and will be the main source of examples for this portion of the article. To give a summary of what the <a target="_blank" href="https://github.com/atsign-foundation/at_demos/tree/master/at_cookbook">at_cookbook</a> demo application is doing, it allows a testable atSign to create a cooking recipe with a recipe name, description, list of ingredients, and a picture. After doing this, the atSign is capable of sharing this entire recipe with another testable atSign. How did we do it? First, we created a dishWidget class that in itself would be an individual recipe with each instance. What we did was first initiate the fields:</p>
<pre><code class="lang-plaintext">// As you can see below, we have the recipe name,
// the ingredients, a description, the image and 
// a location for which screen was previously accessed 
// by the @sign within the app. 
  final String title;
  final String ingredients;
  final String description;
  final String imageURL;
  final String prevScreen;

// We didn’t want the @sign to be capable of passing any 
// of the recipe fields as blank
// [because who doesn’t want the full scoop  of the recipe!] 
// (no pun intended)
// So we made sure to list each field as required
  DishWidget({
    @required this.title,
    @required this.ingredients,
    @required this.description,
    @required this.imageURL,
    @required this.prevScreen,
  });
</code></pre>
<p>After retrieving all of these fields, we then push the entire dish widget as an object to the home screen where the list of recipes is located. Now that you have the recipe itself, you’re able to pass this through the notify verb. When an atSign wishes to share a recipe, all they have to do is press a button. On press of this button, a function titled _share is then called. This function is structured as so:</p>
<pre><code class="lang-plaintext">// Here, we’re initializing a variable called lookup as an AtKey object
// lookup will be our recipe
AtKey lookup = AtKey()

// You’ll notice that we’re defining a couple of the attributes of 
// the AtKey object, mainly the name of the recipe
    ..key = widget.dishWidget.title
//  What @sign is this recipe being shared with?
    ..sharedWith = atSign;

// Of course, we want to ‘get’ the values of our recipe, 
// so we use the get verb which exists in the 
// serverDemoService file of the application
String value = await _serverDemoService.get(lookup);

// ttr (Time To Refresh) being called with a value of -1 
// means that we’re confident that the values
// of our recipe won’t be changing, so once the recipe has been 
// cached on the secondary server that has received the recipe, 
// it will not need to worry about updating the values at any point
// The many fascinating attributes of MetaData will be covered 
// in an article part of this series later down the road!
var metadata = Metadata()..ttr = -1;
// After passing all of the necessary values such as the metadata,
// the appropriate @ signs and the type of notification we’d like
//  to send, we simply pass the values through the notify verb!
      AtKey atKey = AtKey()
        ..key = widget.dishWidget.title
        ..metadata = metadata
        ..sharedBy = atSign
        ..sharedWith = _otherAtSign;

      var operation = OperationEnum.update;
      await _serverDemoService.notify(atKey, value, operation);
</code></pre>
<p>If you would like to have a walkthrough on how we developed the at_cookbook demo app, feel free to watch <a target="_blank" href="https://www.youtube.com/channel/UCzjSJbPmIEDXFbHb2RIj9Gg">Tyler Time Episode 3</a> on YouTube! If you have any questions, be sure to ask us on <a target="_blank" href="https://discord.gg/55sHTQFxfz">Discord</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Easier automation]]></title><description><![CDATA[A few weeks ago Justin Garrison tweeted:

When I try to do something new I’ve been following the same pattern for a decade

Do it in the GUI at least once
Do it manually in the CLI
Automate it with a script or declarative tool like Terraform   
(If n...]]></description><link>https://blog.atsign.dev/easier-automation</link><guid isPermaLink="true">https://blog.atsign.dev/easier-automation</guid><category><![CDATA[Docker]]></category><category><![CDATA[automation]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[@platform]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Mon, 26 Apr 2021 14:56:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619449436569/Hm4SSBP5c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few weeks ago Justin Garrison <a target="_blank" href="https://twitter.com/rothgar/status/1381754373043154946">tweeted</a>:</p>
<blockquote>
<p>When I try to do something new I’ve been following the same pattern for a decade</p>
<ol>
<li>Do it in the GUI at least once</li>
<li>Do it manually in the CLI</li>
<li>Automate it with a script or declarative tool like Terraform   </li>
<li>(If needed) build a declarative API for others to use</li>
</ol>
</blockquote>
<p>This roughly echoes my own approach to automation, but I've been noticing recently that sometimes I'm going straight to step 3 without even trying 1 &amp; 2.</p>
<p>An example would be multi architecture builds for Docker images. We've been doing x86_64 builds for a while, but we also wanted to do 32bit and 64bit Arm for Raspberry Pis, Apple M1, AWS Graviton etc.</p>
<p>We were already using a <a target="_blank" href="https://github.com/features/actions">GitHub Action</a> to create the x86_64 image, and to get that working from the boilerplate below needed just a couple of things:</p>
<ol>
<li>Create the secrets for DOCKERHUB_USERNAME and DOCKERHUB_TOKEN</li>
<li>Fill in the appropriate accountname, imagename and tagname for the <code>tags:</code> line</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Docker</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">workflow_dispatch:</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">docker:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">QEMU</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">docker/setup-qemu-action@v1</span>
      <span class="hljs-bullet">-</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Buildx</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">docker/setup-buildx-action@v1</span>
      <span class="hljs-bullet">-</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Login</span> <span class="hljs-string">to</span> <span class="hljs-string">DockerHub</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">docker/login-action@v1</span> 
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">username:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.DOCKERHUB_USERNAME</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">password:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.DOCKERHUB_TOKEN</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">push</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">docker_build</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">docker/build-push-action@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">push:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">tags:</span> <span class="hljs-string">accountname/imagename:tagname</span>
      <span class="hljs-bullet">-</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Image</span> <span class="hljs-string">digest</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.docker_build.outputs.digest</span> <span class="hljs-string">}}</span>
</code></pre>
<p>To get the Action to make Arm images too all I needed to add was a <code>platforms</code> section after tags:</p>
<pre><code class="lang-yaml">          <span class="hljs-attr">tags:</span> <span class="hljs-string">|
            accountname/imagename:tagname
            accountname/imagename:latest
            accountname/imagename:GHA${{ github.run_number }}
</span>          <span class="hljs-attr">platforms:</span> <span class="hljs-string">|
            linux/amd64
            linux/arm/v7
            linux/arm64/v8</span>
</code></pre>
<p>You'll see that I also added some extra stuff to the tags there to get <code>:latest</code> and a tag associated with the Action run number.</p>
<p>And that's all that was needed. Without ever trying to create a multi-arch image manually, I pressed the run button and it made me images for all 3 architectures. I can't actually imagine how doing it manually would be any easier - even for one iteration never mind for a repeatable system.</p>
<p>NB: I'm using <code>on:  workflow_dispatch:</code> here whilst I test the Action, but soon enough it will be plumbed into the pipeline and running without human touch.</p>
<p>We are changing the Internet for good. Join us on </p>
<blockquote>
<div class="hn-embed-widget" id="discord"></div></blockquote>
<p>and code with us.</p>
]]></content:encoded></item><item><title><![CDATA[Managing GitHub Labels]]></title><description><![CDATA[I was on a sprint planning call last week where it felt like we spent way  too much time getting the labels in our various repos straightened out.

After a little Googling I found various scripts that use the GitHub API to manage labels. But nothing ...]]></description><link>https://blog.atsign.dev/managing-github-labels</link><guid isPermaLink="true">https://blog.atsign.dev/managing-github-labels</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[@platform]]></category><dc:creator><![CDATA[Chris Swan]]></dc:creator><pubDate>Fri, 16 Apr 2021 15:17:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618981150809/J_9pGOtwK.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was on a sprint planning call last week where it felt like we spent <strong>way </strong> too much time getting the labels in our various repos straightened out.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618585959545/Fq1x9Td1Q.jpeg" alt="GH_Labels.jpg" /></p>
<p>After a little Googling I found various scripts that use the GitHub API to manage labels. But nothing that seemed easy enough. So… I pulled together my own repo of scripts –  <a target="_blank" href="https://github.com/atsign-company/labels">atsign-company/labels</a> , which we have now open sourced.</p>
<p>When a label is added to that repo it fires a  <a target="_blank" href="https://github.com/features/actions">GitHub Action</a>  that pushes the new label to all the repos listed in a config file (in our case atsign-foundation.yaml for our open source repos).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618585973830/9hi7cY0eO.jpeg" alt="Labels_Action.JPG" /></p>
<p>Thanks to  <a target="_blank" href="https://www.olivierjacques.com/">Olivier Jacques</a>  for pointing me to the ability to start an Action from a  <a target="_blank" href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows#label">labels event</a> , and to the  <a target="_blank" href="https://github.com/dxc-technology/online-devops-dojo">DXC DevOps Dojo team</a>  for having a nice  <a target="_blank" href="https://github.com/dxc-technology/online-devops-dojo/blob/master/online-devops-dojo/welcome/assets/github-labels.py">labeling script</a>  as part of the Welcome module.</p>
]]></content:encoded></item><item><title><![CDATA[Filtering keys with a regex (Scan with a regex)]]></title><description><![CDATA[Introduction The scan verb in the atPlatform is used to get all the keys present in the secondary server.
Please check the ScanVerbHandler source code to learn more about the implementation of the scan verb.
The focus of this article is to demonstrat...]]></description><link>https://blog.atsign.dev/filtering-keys-with-a-regex-scan-with-a-regex</link><guid isPermaLink="true">https://blog.atsign.dev/filtering-keys-with-a-regex-scan-with-a-regex</guid><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[Regex]]></category><dc:creator><![CDATA[Naresh]]></dc:creator><pubDate>Sat, 10 Apr 2021 15:06:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618067105152/uUuhplxvX.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Introduction The scan verb in the atPlatform is used to get all the keys present in the secondary server.</p>
<p>Please check the ScanVerbHandler source code to learn more about the implementation of the scan verb.</p>
<p>The focus of this article is to demonstrate how to filter scan verb results using a regex. Description If the regex is not specified, the scan verb will return all of the keys applicable to the person running the scan. Let's assume that an app running on the atPlatform is interested in only a subset of the keys. A regex can then be passed as part of the scan verb. When a regex is passed, the atProtocol’s secondary server ensures that only keys matching the regex are returned.</p>
<p>Sample Request:</p>
<pre><code class="lang-plaintext">scan ^@bob
</code></pre>
<p>This request scans all the keys which match with the regex ‘^@bob’</p>
<p>Sample Response:</p>
<pre><code class="lang-plaintext">data:["@bob:firstname@colin","@bob:location@colin"]
</code></pre>
<p>If you are using the SDK, use the following method.</p>
<pre><code class="lang-plaintext">await AtClientImpl.createClient('@bob', '@persona', preference);
var atClient = await AtClientImpl.getClient('@bob’);
List&lt;AtKey&gt; atKeys = await atClient.getAtKeys({String regex, String sharedBy, String sharedWith})
</code></pre>
<p>You can find the actual implementation <a target="_blank" href="https://github.com/atsign-foundation/at_client_sdk/blob/master/at_client/lib/src/client/at_client_impl.dart">here</a> .</p>
<p>Coming Soon</p>
<p>We are working to make the Internet better. Please come code with us! Follow us on github https://github.com/atsign-foundation and join our developer community on Discord</p>
<p>%%[discord]</p>
]]></content:encoded></item><item><title><![CDATA[Test atPlatform apps in one or more physical devices.]]></title><description><![CDATA[Apps require plenty of power for testing in an emulator. On top of that, running docker and perhaps a few background chrome tabs could be devastating for a low-end machine. To avoid this, simply open your vs code (Any lightweight editor will work) ed...]]></description><link>https://blog.atsign.dev/test-atplatform-apps-in-one-or-more-physical-devices</link><guid isPermaLink="true">https://blog.atsign.dev/test-atplatform-apps-in-one-or-more-physical-devices</guid><category><![CDATA[Flutter]]></category><category><![CDATA[app development]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Flutter Examples]]></category><dc:creator><![CDATA[Intiser Ahmed]]></dc:creator><pubDate>Fri, 09 Apr 2021 14:46:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1617168317575/gOOMtPaso.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Apps require plenty of power for testing in an emulator. On top of that, running docker and perhaps a few background chrome tabs could be devastating for a low-end machine. To avoid this, simply open your vs code (Any lightweight editor will work) editor and develop it much quicker than before, with your physical device.</p>
<h3 id="heading-configuring-dns-masquerading-server">Configuring DNS masquerading Server</h3>
<p>You have to first go to your virtual environment folder. If you follow the exact instruction from our getting started page until the advanced section your virtual environment folder will be in the User home folder. Such as in windows : C:\Users\ {Username}\ve . Now open up the “docker-compose.yaml” file in your favorite code editor. Uncomment the lines to enable your DNS masquerading server which can serve DNS and point the DNS address vip.ve.atsign.zone to the hosting PC/MAC/Linux machine.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617166966208/O6Q1ne1z-.png" alt="Screenshot (178).png" /></p>
<p>Then to starts the Dns msq container run ‘docker-compose up -d’ in the terminal.</p>
<p>To find the IP address you need to run ifconfig/ipconfig based on which operating system you are running. Select the Ipv4 address starting with 192 and copy it. Then go to your local DNS Masq server: http://localhost:5380/</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617167257540/9CZkE0cMK.png" alt="unnamed.png" /></p>
<p>edit the address=/vip.ve. atsign.zone line to paste your IP address. Then save it.</p>
<h3 id="heading-configuring-physical-device">Configuring Physical device</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617167382746/FKDvZa4T6.png" alt="pasted image 0.png" /></p>
<p>To configure the physical device, you have to go to the Wi-Fi setting. In android you have to change the IP settings from DHCP to static.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617167400347/hw7Ze8MuI.png" alt="pasted image 0 (1).png" /></p>
<p>Then paste your IP address and save it. You can add as many devices as you want with this setup.</p>
<p>Enjoy developing in <a target="_blank" href="https://atsign.com/">atProtocol</a> !</p>
]]></content:encoded></item></channel></rss>