[{"content":"","permalink":"","summary":"A web app for freelancers and consultants to create, send and manage invoices.","title":"Zippato"},{"content":"","permalink":"","summary":"Find your perfect IRONMAN triathlon. Search and filter IRONMAN, IRONMAN 70.3 and 5150 events worldwide by distance, location, weather and course type.","title":"Find My Tris"},{"content":"","permalink":"","summary":"An iOS app for guided yoga nidra, NSDR and body scan, for deep rest and sleep.","title":"Nidra"},{"content":"","permalink":"","summary":"An open source Elixir client for the Cardano blockchain: query, stream blocks and submit transactions through Ogmios.","title":"Xogmios"},{"content":"","permalink":"","summary":"The JUNGLE stake pool (ticker JNGL) on the Cardano blockchain. I operated it for almost 4 years, from August 2022 until its planned retirement in April 2026. Two-time recipient of a Cardano Foundation delegation in recognition of contributions to the ecosystem.","title":"JNGL - Cardano Stake Pool"},{"content":"","permalink":"","summary":"A YouTube channel that teaches the skills required to work with the Cardano blockchain.","title":"Work With Cardano"},{"content":"Hi, I\u0026rsquo;m Carlos. I\u0026rsquo;m a technologist and I have been writing software professionally for over 20 years.\nWhat I do Consulting. I build software for clients. Products. I build small, focused apps such as Zippato, FindMyTris and Nidra iOS. Teaching. I\u0026rsquo;m a published Pluralsight author with over 50 courses. I also give talks at conferences and meetups. When I\u0026rsquo;m away from the keyboard I\u0026rsquo;m probably making coffee.\nGet in touch Hit me up if you\u0026rsquo;d like to chat: carlos(AT)csouza.me. You can also find me on GitHub and LinkedIn.\n","permalink":"https://csouza.me/about/","summary":"\u003cp\u003eHi, I\u0026rsquo;m Carlos. I\u0026rsquo;m a technologist and I have been writing software professionally for over 20 years.\u003c/p\u003e\n\u003ch2 id=\"what-i-do\"\u003eWhat I do\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eConsulting.\u003c/strong\u003e I build software for clients.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eProducts.\u003c/strong\u003e I build small, focused apps such as \u003ca href=\"https://zippato.com/\"\u003eZippato\u003c/a\u003e, \u003ca href=\"https://findmytris.com/\"\u003eFindMyTris\u003c/a\u003e and \u003ca href=\"https://apps.apple.com/us/app/nidra-yoga-nidra-deep-rest/id6758237848\"\u003eNidra iOS\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTeaching.\u003c/strong\u003e I\u0026rsquo;m a published \u003ca href=\"https://app.pluralsight.com/profile/author/carlos-souza\"\u003ePluralsight author\u003c/a\u003e with over 50 courses. I also give \u003ca href=\"/talks/\"\u003etalks\u003c/a\u003e at conferences and meetups.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhen I\u0026rsquo;m away from the keyboard I\u0026rsquo;m probably making coffee.\u003c/p\u003e\n\u003ch2 id=\"get-in-touch\"\u003eGet in touch\u003c/h2\u003e\n\u003cp\u003eHit me up if you\u0026rsquo;d like to chat: \u003cstrong\u003ecarlos(AT)csouza.me\u003c/strong\u003e. You can also find me on \u003ca href=\"https://github.com/caike\"\u003eGitHub\u003c/a\u003e and \u003ca href=\"https://www.linkedin.com/in/caike/\"\u003eLinkedIn\u003c/a\u003e.\u003c/p\u003e","title":"About"},{"content":"There\u0026rsquo;s a strange irony sitting at the center of modern software development. Personally, since Ruby on Rails came out, this is the moment in my career where I\u0026rsquo;ve felt the most productive and had the most fun. At the same time, I\u0026rsquo;ve never paid more for the privilege.\nI\u0026rsquo;ve been writing software professionally for almost 20 years. When I got started, the barrier to entry was remarkably low. My first tech stack was LAMP. To write PHP + MySQL you could get away with a fairly inexpensive computer, a text editor, and an internet connection. Everything else (the language, the framework, the database, the deployment tools) was both libre and gratis.\nNot \u0026ldquo;free tier\u0026rdquo; free. Actually free.\nOpen source had won, and the whole ecosystem reflected it.\nWant to learn Ruby on Rails? Download it. Need a database? MySQL (pre Oracle), right there. Version control? Git. Hosting for your side project? Heroku (pre Salesforce) free tier, or a cheap Linode VPS. Editor? Vim, Emacs, Notepad++, take your pick. The entire pipeline from idea to deployed application cost you close to nothing.\nThat world shaped a generation of developers, myself included. We tinkered. We broke things on weekends. We built projects nobody asked for, just to see if we could. The open source community wasn\u0026rsquo;t just a resource. It was a culture. You could show up with nothing but enthusiasm and leave with real skills. And for a long time, that kept getting better. New languages, new frameworks, new hosting options, and almost all of it stayed free.\nThe New Stack Has a Subscription Model Fast forward to today, and the developer experience looks radically different. AI-powered tools have genuinely transformed how we work. I use them daily, and I won\u0026rsquo;t pretend otherwise: they\u0026rsquo;re incredible. Having a coding agent that can scaffold a feature, catch bugs, or help me think through architecture decisions in real time has made me meaningfully faster.\nAs of this writing, the first paid tier of either of the two major LLM providers is $20/month, but of course you\u0026rsquo;ll soon run out and quickly find yourself paying $100/month for the next tier, then $200/month for the one after that, until you find yourself creating multiple accounts just so you can Ralph Wiggum to the max. AI-powered editor? $20. Want to experiment with RAG and vector search? Pinecone\u0026rsquo;s Standard plan starts at $50/month. Warp, an AI-powered terminal (because even the damn terminal costs money now!), is $18. And that\u0026rsquo;s before any usage-based API charges for embeddings, inference, or token overages. A developer who wants access to the best available tools can easily spend $100+ per month before writing a single line of business logic.\nNone of these are unreasonable prices individually. But collectively, they represent something new: a recurring cost of entry to the craft that simply didn\u0026rsquo;t exist before.\nWhat This Means for the Next Generation This is where it gets personal for me. I spent years teaching: building courses, running local Coding Dojos, mentoring junior developers. And the thing that made that work was accessibility. I could tell someone, \u0026ldquo;All you need is a laptop. Everything else is free. Let\u0026rsquo;s go.\u0026rdquo;\nI\u0026rsquo;m not sure that\u0026rsquo;s true anymore. Or rather, it\u0026rsquo;s technically true (you can still write code with free tools) but the gap between the free experience and the AI-augmented experience is growing fast.\nA junior developer working without AI assistance is operating at a fundamentally different speed than one who has it. And in a job market that\u0026rsquo;s already tough, that gap matters.\nThink about what it\u0026rsquo;s like to be a new developer today. You\u0026rsquo;re told the industry is competitive. You\u0026rsquo;re told AI is changing everything. You\u0026rsquo;re told we\u0026rsquo;re moments away from the one-person unicorn. You\u0026rsquo;re watching senior developers pair with agents that autocomplete entire projects. And when you sit down to build your first project, you\u0026rsquo;re faced with a choice: invest in tools you might not be able to afford, or fall behind before you\u0026rsquo;ve even started.\nThat\u0026rsquo;s a different on-ramp than the one I had. The open source ethos said, \u0026ldquo;Come as you are.\u0026rdquo; The AI tooling era says, \u0026ldquo;Subscribe to keep up.\u0026rdquo;\nI Don\u0026rsquo;t Have a Clean Answer I\u0026rsquo;m by no means against these tools. I use them quite heavily. I benefit from them. The productivity gains are real, and the companies building them deserve to charge for their work. This isn\u0026rsquo;t a call to make everything free.\nBut I think it\u0026rsquo;s worth pausing to notice what\u0026rsquo;s shifting. The culture of software development, in particular open source, was built on the idea that the tools belong to everyone. That you could go from zero to capable without a credit card. That tinkering was free.\nWe\u0026rsquo;re moving into a world where the best tools are behind paywalls, where staying current means staying subscribed, and where the distance between the haves and have-nots in our industry isn\u0026rsquo;t just about talent. It\u0026rsquo;s about tooling budgets.\nIf you\u0026rsquo;ve been in this industry for a while, that might feel like a small shift. If you\u0026rsquo;re just getting started, it might feel like the door is a little harder to open than you were told it would be.\nI think that\u0026rsquo;s worth talking about.\nOriginally published on Medium.\n","permalink":"https://csouza.me/posts/the-rising-cost-of-being-an-ai-powered-developer/","summary":"\u003cp\u003eThere\u0026rsquo;s a strange irony sitting at the center of modern software development. Personally, since \u003ca href=\"https://www.youtube.com/watch?v=Gzj723LkRJY\"\u003eRuby on Rails\u003c/a\u003e came out, this is the moment in my career where I\u0026rsquo;ve felt the most productive and had the most fun. At the same time, \u003cstrong\u003eI\u0026rsquo;ve never paid more for the privilege.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve been writing software professionally for almost 20 years. When I got started, the barrier to entry was remarkably low. My first tech stack was \u003ca href=\"https://en.wikipedia.org/wiki/LAMP_%28software_bundle%29\"\u003eLAMP\u003c/a\u003e. To write PHP + MySQL you could get away with a fairly inexpensive computer, a text editor, and an internet connection. Everything else (the language, the framework, the database, the deployment tools) was both \u003ca href=\"https://en.wikipedia.org/wiki/Gratis_versus_libre\"\u003elibre and gratis\u003c/a\u003e.\u003c/p\u003e","title":"The Rising Cost of Being an AI-Powered Developer"},{"content":"Se você está acompanhando a evolução dos agentes de código com IA, provavelmente já se deparou com uma sopa de letrinhas e conceitos que parecem ter sido inventados na semana passada: Prompts, Rules, Tools, MCP, Skills…\nA boa notícia é que esses conceitos não são tão complicados quanto parecem. A má notícia? Quase ninguém explica direito a diferença entre eles. Então nesse post eu vou te ajudar com isso.\nComeçando pelo Básico: Prompts Prompts são as instruções em linguagem natural que você dá para o agente. É literalmente o que você quer que ele faça.\nSe você já usou ChatGPT, Claude ou qualquer outro chat bot, você já escreveu prompts. Simples assim.\n\u0026ldquo;Analise esse código e me diga se tem algum problema de segurança: [código]\u0026rdquo;\nIsso é um prompt. Não tem mágica. E aqui vai uma dica:\nO que torna um prompt eficaz é a sua especificidade.\nEm vez de \u0026ldquo;revise meu código\u0026rdquo;, você escreve \u0026ldquo;atue como um engenheiro de segurança sênior, analise esse código Elixir focando em vulnerabilidades de injeção em queries Ecto e exposição de dados sensíveis em logs, e formate a resposta como uma lista de issues com severidade.\u0026rdquo;\nQuando usar prompts: Sempre. É o ponto de partida de qualquer interação com agentes.\nRules: O Comportamento no Piloto Automático Agora as coisas começam a ficar mais interessantes. Rules são restrições persistentes que moldam como o agente se comporta ao longo do tempo e não apenas em uma única interação. É uma forma de memória do agente.\nNo Claude Code (nosso agente de escolha), por exemplo, temos algumas formas diferentes de definir rules. As duas mais importantes são:\n1. O arquivo CLAUDE.md na raiz do projeto — perfeito para o essencial:\nThis is a web application written using the Phoenix web framework. ## Project guidelines - Use `mix precommit` alias when you are done with all changes and fix any pending issues - Use the already included and available `:req` (`Req`) library for HTTP requests, **avoid** `:httpoison`, `:tesla`, and `:httpc`. Req is included by default and is the preferred HTTP client for Phoenix apps - ALWAYS write tests for new features - NEVER create commits directly on the main branch - ... 2. O diretório ./claude/rules/ — para rules mais específicas e organizadas:\n.claude/ └── rules/ ├── testing.md # Convenções de testes ├── security.md # Práticas de segurança └── database.md # Padrões de acesso a dados A sacada aqui é manter o CLAUDE.md enxuto com as regras fundamentais do projeto, enquanto rules mais detalhadas ou específicas de domínio ficam organizadas em arquivos separados.\nA diferença crucial: prompts são instruções pontuais, rules (como já diz o nome) são regras que os prompts seguem durante a sua execução.\nPara mais informações sobre rules e diferentes formas de defini-las, visite a seção de Memory Management na documentação do Claude Code.\nQuando usar rules: Quando você quer consistência no projeto sem ter que repetir as mesmas instruções em seus prompts.\nTools: Dando Mãos para a IA Até aqui, o agente só pensa e escreve de acordo com o conteúdo que foi utilizado durante o treinamento dos seus respectivos modelos. Tools são o que permitem ele agir no mundo real.\nUma tool é uma função que o agente pode invocar: ler arquivos, executar comandos no terminal, fazer requests HTTP, consultar bancos de dados.\nNo Claude Code, você tem tools como:\nRead — ler arquivos e diretórios Edit — modificar código existente Bash — executar comandos no terminal WebSearch — buscar informações na internet O fluxo é sempre o mesmo:\nO agente decide que precisa usar uma tool Chama a tool com os parâmetros necessários Recebe o resultado Usa esse resultado para continuar o raciocínio É isso que transforma um chatbot em um agente autônomo. Sem tools, a IA é um cérebro sem corpo.\nQuando usar tools: Quando o modelo precisa fazer algo além de gerar texto, tal como ler, escrever, executar, buscar.\nMCP: O USB dos Agentes de IA Ok, tools são poderosas. Mas e se você precisar conectar seu agente a diferentes serviços externos? O prompt a seguir, por exemplo:\nListe a issue mais recente desse projeto no Github e implemente uma solução\nSerá que é necessário implementar uma integração com o Github? É aí que entra o MCP.\nModel Context Protocol (MCP) é um protocolo aberto que padroniza como agentes se conectam a sistemas externos. Ele é como a evolução natural das tools: em vez de definir cada função manualmente, você conecta a um MCP Server e ganha acesso a todas as tools que ele expõe podendo utilizá-las dinamicamente.\nQuer conectar seu agente ao GitHub? Tem MCP server para isso. Figma? Tem. Seu banco Postgres? Também. Algum sistema interno proprietário? Você pode criar um MCP server para ele.\nClaude Code \u0026lt;--MCP--\u0026gt; GitHub Server \u0026lt;--\u0026gt; API do GitHub | +--\u0026gt; Figma Server \u0026lt;--\u0026gt; API do Figma | +--\u0026gt; Sistema Interno \u0026lt;--\u0026gt; Seus sistemas É como se fosse um padrão USB para IA. Qualquer cliente MCP conecta em qualquer server MCP. Plug and play.\nA grande sacada é que o agente descobre as tools disponíveis em runtime. Você não precisa recompilar, redeployar ou reconfigurar nada. Adicionou um novo MCP server? As tools dele já estão disponíveis ao agente.\nQuando usar MCP: Quando você quer integrações reutilizáveis e padronizadas, especialmente se está construindo uma plataforma que outros vão estender.\nSkills: O Manual de Boas Práticas On-Demand Se você acompanha as novidades do ecossistema de agente de IA, provavelmente viu esse conceito explodir nas últimas semanas. Skills viraram o assunto do momento.\nAqui está um conceito que muita gente confunde com prompts ou rules. Skills são pacotes de conhecimento especializado que o agente consulta sob demanda, antes de executar uma tarefa específica.\nNa prática, são arquivos markdown que contêm:\nMelhores práticas para uma tarefa específica Padrões de código que funcionam bem Armadilhas comuns a evitar Exemplos de uso Você pede para o Claude Code escrever testes para alguma funcionalidade na sua aplicação Phoenix, por exemplo:\nEscreva testes para a funcionalidade de suspensão de usuários abusivos\nCaso o agente tenha acesso a um arquivo SKILL.md dentro de uma pasta elixir-phoenix-tests e cujo frontmatter informe que a skill é relacionada a testes no Phoenix, o agente automaticamente trará o conteúdo do markdown para sua janela de contexto. Um exemplo de skills para testes em Phoenix é o seguinte:\nWhen writing tests: - Default tests to async: true - NEVER call Application.put_env (Caso queira saber por que NUNCA devemos chamar Application.put_env em nossos testes, eu explico nesse vídeo a seguir:)\nUm grande benefício de utilizarmos skills é manter a nossa janela de contexto enxuta. Em vez de carregar conteúdo muitas vezes desnecessário em toda interação, o agente busca apenas o que precisa, quando precisa.\nA Vercel lançou recentemente um portal de busca e consulta das skills mais utilizadas pela comunidade: https://skills.sh/\nQuando usar Skills: Para tarefas especializadas que requerem conhecimento específico de bibliotecas, formatos ou domínios.\nJuntando Tudo: Um Exemplo Real Imagine que você pede para o agente: \u0026ldquo;Atualize o onboarding de novos usuários para puxar o endereço automaticamente a partir do CEP.\u0026rdquo;\nPrompt: A instrução que você acabou de dar. Clara, específica, com contexto suficiente para o agente entender o objetivo. Rules (no CLAUDE.md): O agente varre as regras no CLAUDE.md e se depara com a seguinte regra: \u0026ldquo;Use a API dos Correios para consultas de CEP. Siga o padrão de contextos do projeto em lib/my_app/contexts/.\u0026rdquo; Tools: O agente usa Read para ler o código atual do onboarding, Bash para rodar os testes existentes, Edit para implementar a integração com a API de CEP, e WebSearch para consultar a documentação da API. Skills: O agente lembra que precisa escrever testes, nota que existe uma skill relacionada a testes no Phoenix, e carrega essa skill antes de escrever os testes. MCP: Conecta no MCP server do GitHub para criar um branch, abrir um PR com a implementação, e no server do Slack para avisar o time que a feature está pronta para review - e de repente até posta uma imagem engraçadinha também :-) Conclusão A verdade é que daqui a alguns meses esse post provavelmente vai precisar de uma atualização. E tudo bem, faz parte do jogo.\nO mais importante não é decorar cada conceito, mas entender que esses agentes possuem uma arquitetura por trás. Não é mágica. É engenharia.\nMas os fundamentos que vimos aqui com Prompts, Rules, Tools, MCP e Skill, formam a base de tudo que está sendo construído. Entender essas camadas hoje é o que vai separar quem usa IA de quem realmente tira proveito dela.\nSe você ainda não experimentou essas técnicas com o Claude Code, comece simples: crie um CLAUDE.md no seu projeto e veja o que acontece. E para ler mais a fundo sobre esses conceitos e aprender como o Claude Code funciona, visite o site oficial https://code.claude.com/docs/en/how-claude-code-works\nE você, como está usando agentes de código no seu time? Já experimentou Claude Code ou outras ferramentas? Quais padrões funcionaram (ou falharam miseravelmente) para vocês?\nCompartilha aí nos comentários!\nPublicado originalmente na Idopter Labs.\n","permalink":"https://csouza.me/posts/prompts-rules-tools-mcp-e-skills/","summary":"\u003cp\u003eSe você está acompanhando a evolução dos agentes de código com IA, provavelmente já se deparou com uma sopa de letrinhas e conceitos que parecem ter sido inventados na semana passada: Prompts, Rules, Tools, MCP, Skills…\u003c/p\u003e\n\u003cp\u003eA boa notícia é que esses conceitos não são tão complicados quanto parecem. A má notícia? Quase ninguém explica direito a diferença entre eles. Então nesse post eu vou te ajudar com isso.\u003c/p\u003e\n\u003ch2 id=\"começando-pelo-básico-prompts\"\u003eComeçando pelo Básico: Prompts\u003c/h2\u003e\n\u003cp\u003ePrompts são as instruções em linguagem natural que você dá para o agente. É literalmente \u003cem\u003eo que\u003c/em\u003e você quer que ele faça.\u003c/p\u003e","title":"Prompts, Rules, Tools, MCP e Skills: Decifrando a Sopa de Letrinhas dos Coding Agents"},{"content":"Introduction Consider a monitoring system for a weather station. A sensor in the field collects temperature readings every hour and reports them back to the system, but occasionally goes offline due to power outages or maintenance. When generating daily reports, the data shows gaps.\nQuerying the readings table for a particular day returns only the hours when the sensor was operational. Asking users to figure out which hours are missing feels like poor UX.\nIn this article we\u0026rsquo;ll learn how to leverage PostgreSQL\u0026rsquo;s generate_series() function to tackle this problem and how to use it in a Phoenix application with Ecto.\nThe generate_series() function PostgreSQL\u0026rsquo;s generate_series() function produces a set of values within a specified range. Here\u0026rsquo;s the signature of this function:\ngenerate_series( start_value, stop_value, step_value ) And here\u0026rsquo;s how we will use it:\nSELECT generate_series( \u0026#39;2024-12-21 00:00:00\u0026#39;::timestamptz, -- first hour of the day \u0026#39;2024-12-21 23:00:00\u0026#39;::timestamptz, -- last hour of the day \u0026#39;1 hour\u0026#39;::interval -- step through each of the 24 hours of the day ) AS hour; -- alias for easier reference This command returns 24 rows. One row for each hour of December 21, 2024 (the date will be provided as input by the user):\nhour ------------------------ 2024-12-21 00:00:00+00 2024-12-21 01:00:00+00 2024-12-21 02:00:00+00 ... 2024-12-21 23:00:00+00 Data with Gaps The temperature_readings table is used for storing hourly readings, with columns reading_time and temp_celsius. On a normal day, we\u0026rsquo;d have 24 records. However, if the sensor was offline between 02:30 and 04:30, for example, then we\u0026rsquo;d only have 19 records. Here\u0026rsquo;s a simple SQL query that returns all readings for 2024-12-21:\nSELECT reading_time, temp_celsius FROM temperature_readings WHERE reading_time::date = \u0026#39;2024-12-21\u0026#39; ORDER BY reading_time; Result (simplified):\nreading_time | temp_celsius -----------------------+-------------- 2024-12-22 00:00:00 | -3.9 2024-12-22 01:00:00 | -4.2 2024-12-22 02:00:00 | -4.5 -- 03:00 and 04:00 missing! 🤨 2024-12-22 05:00:00 | -4.5 2024-12-22 06:00:00 | -4.5 Combining generate_series() with a LEFT JOIN By combining generate_series() with a LEFT JOIN we can ensure ALL hours appear in the result:\nSELECT hours.hour::timestamptz AS reading_time, t.temp_celsius FROM generate_series( \u0026#39;2024-12-21 00:00:00\u0026#39;::timestamptz, -- first hour of the day \u0026#39;2024-12-21 23:00:00\u0026#39;::timestamptz, -- last hour of the day \u0026#39;1 hour\u0026#39;::interval ) AS hours(hour) LEFT JOIN temperature_readings t ON t.reading_time = hours.hour::timestamptz ORDER BY hours.hour; The LEFT JOIN ensures that all rows from the \u0026ldquo;left\u0026rdquo; table (hours table) are included in the result even if a matching record on the joining table (temperature_readings table) does not exist. In other words, we need a row for every hour of the day (left table) even if a reading for that hour (right table) is missing.\nThe query gives us a result that includes all 24 hours, with NULL values where there\u0026rsquo;s no data:\nreading_time | temp_celsius -----------------------+-------------- 2024-12-21 00:00:00 | -3.9 2024-12-21 01:00:00 | -4.2 2024-12-21 02:00:00 | -4.5 2024-12-21 03:00:00 | NULL 2024-12-21 04:00:00 | NULL 2024-12-21 05:00:00 | -4.5 2024-12-21 06:00:00 | -4.5 This is a much easier result set for us to work with from our application. We will take it up a notch by including a status column based on the value of the temp_celsius column:\nSELECT hours.hour::timestamptz AS reading_time, t.temp_celsius, CASE WHEN t.temp_celsius IS NULL THEN \u0026#39;MISSING\u0026#39; ELSE \u0026#39;OK\u0026#39; END AS status FROM generate_series( \u0026#39;2024-12-21 00:00:00\u0026#39;::timestamptz, \u0026#39;2024-12-21 23:00:00\u0026#39;::timestamptz, \u0026#39;1 hour\u0026#39;::interval ) AS hours(hour) LEFT JOIN temperature_readings t ON t.reading_time = hours.hour::timestamptz ORDER BY hours.hour; And the result now includes a third column that we can directly reference from our application:\nreading_time | temp_celsius | status -----------------------+--------------+--------- 2024-12-21 00:00:00 | -3.9 | OK 2024-12-21 01:00:00 | -4.2 | OK 2024-12-21 02:00:00 | -4.5 | OK 2024-12-21 03:00:00 | NULL | MISSING 2024-12-21 04:00:00 | NULL | MISSING 2024-12-21 05:00:00 | -4.5 | OK 2024-12-21 06:00:00 | -4.5 | OK Building the Query in Ecto Now let\u0026rsquo;s implement this logic in Elixir using Ecto. The key is the fragment/1 function, which allows us to embed raw SQL within Ecto queries.\nFirst, we define our schema for temperature readings:\ndefmodule Reports.TemperatureReading do use Ecto.Schema @primary_key {:id, :id, autogenerate: true} schema \u0026#34;temperature_readings\u0026#34; do field :reading_time, :utc_datetime field :temp_celsius, :decimal end end And here\u0026rsquo;s the complete implementation of the module that fetches hourly readings, where the get_hourly_readings/1 function implements the Ecto version of the aforementioned SQL query:\ndefmodule Reports.Temperatures do import Ecto.Query alias Reports.Repo alias Reports.TemperatureReading def get_hourly_readings(date) do start_time = get_start_time(date) end_time = get_end_time(date) query = from( all_times in fragment( \u0026#34;generate_series(?::timestamptz, ?::timestamptz, \u0026#39;1 hour\u0026#39;::interval)\u0026#34;, ^start_time, ^end_time ), left_join: tr in TemperatureReading, on: all_times == tr.reading_time, select: %{ reading_time: fragment(\u0026#34;?::timestamptz\u0026#34;, all_times), temp_celsius: tr.temp_celsius, status: fragment( \u0026#34;CASE WHEN ? IS NULL THEN \u0026#39;MISSING\u0026#39; ELSE \u0026#39;OK\u0026#39; END\u0026#34;, tr.temp_celsius ) }, order_by: [asc: all_times] ) Repo.all(query) end # get_start_time/1 and get_end_time/1 omitted for brevity end Breaking this down:\nfragment: We use fragment/1 to call the PostgreSQL generate_series() function directly. The ? placeholders are replaced by the ^start_time and ^end_time parameters and ::timestamptz casts the values to timestamps with timezone set to UTC.\nleft_join: The LEFT JOIN ensures that all generated hours appear in the result, even those without a match in the temperature_readings table.\nThe on condition: We directly compare the generated value (all_times) with the reading_time field from the schema.\nselect with fragment: We select the columns we want to read from the result and use fragment again for the CASE WHEN, which calculates each row\u0026rsquo;s status.\n⚠️ Consistent timezone handling is critical. Unless you have a strong reason NOT to, you should ALWAYS use UTC throughout your application to avoid daylight saving time complications.\nRendering the Reports The big advantage of this approach is simplicity in the presentation layer. Since the query returns all 24 hours with an explicit status field, the template can iterate directly without complex logic. Here\u0026rsquo;s what the controller looks like:\ndefmodule ReportsWeb.ReportController do use ReportsWeb, :controller alias Reports.Temperatures def show(conn, %{\u0026#34;date\u0026#34; =\u0026gt; date}) do date = sanitize_date(date) readings = Temperatures.get_hourly_readings(date) render(conn, :show, readings: readings, date: date) end end The Template In the HEEx template, iteration is straightforward:\n\u0026lt;table\u0026gt; \u0026lt;tbody\u0026gt; \u0026lt;%= for reading \u0026lt;- @readings do %\u0026gt; \u0026lt;tr class={reading_class(reading.status)}\u0026gt; \u0026lt;td\u0026gt;{Calendar.strftime(reading.reading_time, \u0026#34;%H:%M\u0026#34;)}\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;{display_temperature(reading.temp_celsius)}\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt; \u0026lt;span class={status_class(reading.status)}\u0026gt; {reading.status} \u0026lt;/span\u0026gt; \u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;% end %\u0026gt; \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; With this technique, our report now displays all 24 hours with clear visual indicators for missing data.\nThis approach brings several benefits to the HEEx template. A single loop traverses all 24 hours without conditional checks for missing data. The explicit status field enables straightforward styling decisions in the reading_class and status_class helper functions. It makes for a much simpler HEEx template with the cost of a slightly more sophisticated query. Low cost for a big benefit!\nConclusion The combination of generate_series() with LEFT JOIN is a powerful technique for working with time-series data that may have gaps. By moving this logic to the database, we can significantly simplify the application code.\nTry implementing this approach in your next PostgreSQL backed Phoenix project and see how it simplifies report generation!\nOriginally published on Idopter Labs.\n","permalink":"https://csouza.me/posts/handling-gaps-in-time-series-data-with-postgres-and-ecto/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eConsider a monitoring system for a weather station. A sensor in the field collects temperature readings every hour and reports them back to the system, but occasionally goes offline due to power outages or maintenance. When generating daily reports, the data shows gaps.\u003c/p\u003e\n\u003cp\u003eQuerying the readings table for a particular day returns only the hours when the sensor was operational. Asking users to figure out which hours are missing feels like poor UX.\u003c/p\u003e","title":"Handling Gaps in Time-Series Data with PostgreSQL's generate_series() and Ecto"},{"content":"A Idopter Labs conquistou o terceiro lugar em uma hackathon de Agentes de Inteligência Artificial, desenvolvendo uma prova de conceito inovadora que conecta inteligência artificial à blockchain. O prêmio foi concedido pela Masumi Network, uma organização alemã que está criando uma rede financeira voltada para Agentes de IA.\nColaboração entre Agentes Estudos recentes na área de IA apontam que o maior avanço nos sistemas de agentes inteligentes não vem apenas da evolução dos grandes modelos de linguagem (LLMs como GPT, Gemini e DeepSeek), mas da capacidade de fazer agentes trabalharem em equipe de forma colaborativa. Quando colaboram, eles conseguem chegar a soluções mais rápido e com muito mais qualidade do que quando atuam sozinhos.\nA solução desenvolvida durante a hackathon utilizou o framework multi-agentes CrewAI. O CrewAI é um framework open-source em crescente adoção no mercado que permite a criação de equipes de Agentes de IA especializados e capazes de trabalhar juntos em tarefas complexas.\nA rede da Masumi, atualmente em fase de testes, funciona como um marketplace descentralizado onde Agentes de IA podem contratar serviços uns dos outros de forma autônoma, com todas as decisões registradas na blockchain para que possam ser verificadas e auditadas, promovendo maior segurança e transparência.\nEntendendo a Solução A solução desenvolvida pela Idopter Labs é totalmente open-source e está organizada em dois repositórios complementares no Github:\nMasumi Cardano Agent — O programa multi-agente que gera relatórios de tokens Masumi Agent API — Interface para integração com a rede Masumi O programa multi-agente utiliza uma arquitetura baseada no CrewAI para analisar endereços da blockchain da Cardano e gerar relatórios detalhados sobre tokens. Para isso, foram desenvolvidas ferramentas especializadas que ampliam as capacidades dos agentes, permitindo-lhes consultar APIs externas e enriquecer o contexto disponível para os modelos de linguagem.\nA solução integra dois serviços fundamentais do ecossistema Cardano:\nKupo: Um indexador de blockchain que oferece consulta rápida e eficiente de saldos e UTXOs (Unspent Transaction Outputs) Token Registry: Um registro centralizado que fornece metadados detalhados sobre tokens nativos da Cardano Combinando esses serviços, o sistema consegue identificar e reportar os tokens com maior volume em cada carteira analisada.\nO segundo repositório é uma API web que expõe o serviço de relatório ao marketplace da Masumi seguindo o padrão MIP-003 (Masumi Improvement Proposal 003), que define a especificação técnica para contratação automatizada de serviços entre agentes na rede.\nComo você pode observar na documentação da Masumi, o processo é não trivial. Ele envolve diferentes interações entre a API web e a rede Masumi, e também interações com smart contracts. Apesar da complexidade aparente, a experiência de desenvolvimento é significativamente simplificada graças à biblioteca de SDK da Masumi e ao Masumi Payment Service, que abstraem as interações mais complexas com smart contracts e a rede blockchain.\nObservabilidade e Otimização de Custos O monitoramento de custos é um aspecto crítico em projetos que utilizam APIs de LLMs pagas, onde cada chamada ao modelo representa um custo direto. Para gerenciar efetivamente esses gastos, implementamos observabilidade completa através do Langtrace, uma ferramenta de monitoramento suportada nativamente pelo CrewAI.\nO Langtrace nos permite acompanhar em tempo real não apenas custos, mas também latência, taxa de sucesso das chamadas e padrões de uso dos tokens, fornecendo insights essenciais para otimização contínua.\nPara este projeto, escolhemos a API da OpenAI devido à sua integração nativa com o CrewAI e à variedade de modelos disponíveis. A OpenAI oferece uma gama de opções — desde modelos otimizados para custo-benefício (como o GPT-4o-mini) até versões com capacidades avançadas de reasoning (como o GPT-4o), permitindo escolher a solução mais adequada para cada caso de uso. Para desenvolvimento, o modelo 4o-mini é suficiente e também o modelo padrão utilizado pelo CrewAI.\nPara avaliar o custo-benefício de diferentes modelos, realizamos testes comparativos com cenários idênticos.\nResultados de uma execução do nosso programa utilizando o modelo GPT 4o-mini:\nTempo de processamento: 117 segundos Tokens consumidos: ~30k Custo total: US$ 0,03 Resultados de uma nova execução utilizando o modelo GPT 4o:\nTempo de processamento: 80 segundos (-46%) Tokens consumidos: ~28k Custo total: US$ 0,42 (+1300%) A análise comparativa revela um trade-off interessante: embora o GPT-4o seja aproximadamente 46% mais rápido, seu custo é 14 vezes superior ao GPT-4o-mini. Com base nesta análise de custo-benefício e considerando que nossa aplicação não exige velocidade extrema nem capacidades avançadas de reasoning, optamos por manter o GPT-4o-mini em produção. Esta decisão demonstra a importância de testar diferentes modelos e monitorar métricas de performance para otimizar custos sem comprometer a qualidade da solução.\n💡 Dica: Sempre inicie com modelos mais econômicos durante o desenvolvimento e teste modelos mais avançados apenas quando necessário. A economia pode ser significativa em projetos de larga escala!\nConclusão A participação da Idopter Labs na hackathon da Masumi Network demonstra o potencial transformador dos Agentes de IA colaborativos quando integrados com tecnologia blockchain. Nossa solução validou uma abordagem prática e eficiente para automatizar análises de dados on-chain usando o framework CrewAI.\nOs resultados obtidos — desde a arquitetura multi-agente até a otimização de custos com modelos de LLM — ilustram que é possível desenvolver soluções robustas e economicamente viáveis para o mercado de agentes autônomos.\nMais do que um projeto de hackathon, esta solução representa um marco na jornada da Idopter Labs em direção à adoção mainstream de agentes de IA colaborativos, demonstrando que o futuro dos negócios automatizados já é uma realidade tangível.\nEstá interessado em implementar agentes de IA colaborativos em sua empresa? A Idopter Labs oferece consultoria especializada em desenvolvimento de soluções multi-agentes personalizadas para seus desafios específicos.\nPublicado originalmente na Idopter Labs.\n","permalink":"https://csouza.me/posts/agentes-de-ia-colaborativos-crewai-masumi/","summary":"\u003cp\u003eA \u003ca href=\"https://www.idopterlabs.com.br/\"\u003eIdopter Labs\u003c/a\u003e conquistou o terceiro lugar em uma \u003cem\u003ehackathon\u003c/em\u003e de Agentes de Inteligência Artificial, desenvolvendo uma prova de conceito inovadora que conecta inteligência artificial à blockchain. O prêmio foi concedido pela \u003ca href=\"https://www.masumi.network/\"\u003eMasumi Network\u003c/a\u003e, uma organização alemã que está criando uma rede financeira voltada para Agentes de IA.\u003c/p\u003e\n\u003ch2 id=\"colaboração-entre-agentes\"\u003eColaboração entre Agentes\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://arxiv.org/abs/2307.07924\"\u003eEstudos recentes na área de IA\u003c/a\u003e apontam que o maior avanço nos sistemas de agentes inteligentes não vem apenas da evolução dos grandes modelos de linguagem (LLMs como GPT, Gemini e \u003cem\u003eDeepSeek\u003c/em\u003e), mas da capacidade de fazer agentes \u003cstrong\u003etrabalharem em equipe de forma colaborativa.\u003c/strong\u003e Quando colaboram, eles conseguem chegar a soluções mais rápido e com muito mais qualidade do que quando atuam sozinhos.\u003c/p\u003e","title":"Agentes de IA Colaborativos: Um Case Study com CrewAI e Masumi Network"},{"content":"Effective Date: August 2024\n1. Introduction In SWODS App, your privacy is our priority. We are committed to protecting your personal information and ensuring transparency about how we collect, use, and share data related to your exercise activities.\n2. Information We Collect We collect and process the following information:\nPersonal Information: This includes your name, email address, and other contact details you provide when creating an account.\nExercise Data: This includes details about your workouts, exercise routines, and fitness goals.\nThird-Party Data: We may receive additional data from Garmin via their API to enhance your user experience.\n3. How We Use Your Information We use the collected data to:\nProvide and improve our services, ensuring they meet your fitness needs. Personalize your experience, including workout recommendations and progress tracking. Connect to Garmin services to sync your exercise data and enhance the overall functionality of the app. 4. Sharing Your Information We do not share your personal information with third parties, except as necessary to connect with Garmin\u0026rsquo;s API. We ensure that all data sharing is done securely and in compliance with applicable laws.\n5. Data Security We implement industry-standard security measures to protect your data. This includes encryption, secure storage, and regular security audits.\n6. Your Rights You have the right to access, update, or delete your personal information at any time. Please contact us if you wish to exercise these rights.\n7. Changes to This Policy We may update this Privacy Policy from time to time. We will notify you of any significant changes via email or through the app.\n8. Contact Us If you have any questions or concerns about this Privacy Policy, please contact us at [Your Contact Information].\n9. Compliance with Garmin\u0026rsquo;s Policies Our integration with Garmin complies with their privacy standards and API usage policies. We are committed to safeguarding the integrity of your data in alignment with Garmin\u0026rsquo;s requirements.\n","permalink":"https://csouza.me/swods-privacy/","summary":"\u003cp\u003e\u003cstrong\u003eEffective Date:\u003c/strong\u003e August 2024\u003c/p\u003e\n\u003ch2 id=\"1-introduction\"\u003e1. Introduction\u003c/h2\u003e\n\u003cp\u003eIn \u003cstrong\u003eSWODS App\u003c/strong\u003e, your privacy is our priority. We are committed to protecting your personal information and ensuring transparency about how we collect, use, and share data related to your exercise activities.\u003c/p\u003e\n\u003ch2 id=\"2-information-we-collect\"\u003e2. Information We Collect\u003c/h2\u003e\n\u003cp\u003eWe collect and process the following information:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePersonal Information:\u003c/strong\u003e This includes your name, email address, and other contact details you provide when creating an account.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExercise Data:\u003c/strong\u003e This includes details about your workouts, exercise routines, and fitness goals.\u003c/p\u003e","title":"Privacy Policy for SWODS App"},{"content":"Em setembro, Orlando voltou a ser palco da ElixirConf US após 7 anos desde sua última edição na cidade. Tive o privilégio de participar novamente desse encontro de três dias, repleto de palestras sobre variados temas relacionados ao ecossistema Elixir e BEAM, organizadas em três trilhas simultâneas. A qualidade das palestras foi tão alta que se tornou um desafio decidir qual assistir! A seguir, faço um resumo das que tive a oportunidade de acompanhar.\nDia 1 O evento começou com a Keynote do Chris McCord, criador do Phoenix e LiveView. Em sua palestra, Chris apresentou o progresso de ambos os projetos e algumas das novidades que estão por vir. O vídeo de sua apresentação encontra-se disponível no canal da ElixirConf no Youtube, mas gostaria de mencionar uma das features que mais me chamou atenção: uma nova API para gerenciamento de tarefas assíncronas em LiveView. Essa nova API é uma abstração em cima de padrões para tarefas assíncronas em LiveView, e ajuda principalmente no tratamento de \u0026ldquo;edge-cases\u0026rdquo; que costumam ser deixados de lado ou implementados de forma incorreta.\nApós a Keynote, assisti a palestra da Elaine Watanabe. Já havia assistido algumas palestras virtuais da Elaine e também escutado sua entrevista no podcast Elixir em Foco, então foi um prazer poder finalmente conhecê-la pessoalmente. Em sua palestra, Elaine falou sobre \u0026ldquo;code smells\u0026rdquo; em Elixir e apresentou diversas soluções para tornar nosso código Elixir mais facilmente compreensível e manutenível.\nEm seguida veio a palestra de Charlie Holtz sobre como utilizar Elixir para criar aplicações que utilizam Inteligência Artificial. Charlie mostrou como padrões de aplicações IA conseguem ser mapeados para padrões comumente utilizados em Elixir e OTP.\nUtilizando a API do https://replicate.com/, Charlie demonstrou um exemplo de rede social onde cada um dos participantes é um agente AI com comportamento próprio e único. Um dos agentes criados durante sua palestra foi o \u0026ldquo;José Valim\u0026rdquo; 😆 No geral, uma excelente apresentação com muita descontração!\nEm \u0026ldquo;State: A necessary Evil\u0026rdquo;, Isaac Yonemoto apresentou 11 formas diferentes de trabalharmos com estado persistente em Elixir. Para cada uma das formas, Isaac apresentou benefícios e malefícios, seguido de exemplos práticos e de simples compreensão. Descobri durante sua palestra que Isaac é o criador do projeto Zigler, que permite utilizar código Zig diretamente de aplicações Elixir através de Native Implemented Functions (NIFs).\nA palestra seguinte foi a de Savannah Manning. Ela contou sua história de mudança de carreira de turismo em escalada para o mercado de desenvolvimento de software, e deu algumas dicas de como desenvolvedores iniciantes podem evoluir em suas carreiras (livros, sites, práticas, etc.).\nAJ Foster foi o palestrante seguinte, e falou sobre alguns obstáculos enfrentados com as atuais soluções de geração de código para APIs. AJ apresentou seu projeto Open API Generator que almeja solucionar alguns desses obstáculos.\nEm seguida, Andrew Selder apresentou estratégias para compilação de templates dinâmicos em aplicações LiveView. As técnicas apresentadas por Andrew envolvem aplicação (cautelosa) de metaprogramação em Elixir utilizadas em projetos da Estee Lauder.\nA palestra final do dia 1 foi de Brian Cardarella, CEO da DockYard. Brian apresentou o progresso do LiveView native, uma solução para desenvolvimento de aplicações móveis em Elixir em LiveView. Apesar de React Native e Flutter servir-nos muito bem para nossos apps na Idopter Labs, estamos sempre abertos a novas formas de melhorar nosso trabalho e por isso vamos manter o LiveView Native no radar.\nDia 2 A primeira palestra do segundo dia foi de Katelynn Burns, que apresentou uma aplicação de \u0026ldquo;motion tracking\u0026rdquo; utilizando Bumblebee e LiveView. Bumblebee é uma biblioteca Elixir construída em cima do Nx, que permite trabalharmos com modelos existentes de redes neurais de forma extremamente simples. Kate mostrou em sua palestra como podemos adaptar modelos do HuggingFace para casos de uso específicos.\nA palestra seguinte foi de Jenny Bramble, que deu uma verdadeira aula de técnicas de testes que podem ser utilizadas por times de desenvolvimento para aumentar a cobertura e eficiência de testes Black Box.\nUma palestra bem técnica mas muito interessante foi a de Rafał Studnicki, que falou sobre como a sua empresa Whatnot faz deployment de serviços \u0026ldquo;stateful\u0026rdquo; em Elixir. Apesar de Elixir e Phoenix facilitarem bastante o desenvolvimento de aplicações \u0026ldquo;real-time\u0026rdquo;, a parte de deployment ainda impõe diversos desafios. Rafał apresentou estratégias de rolling deployments preservando conexões existentes e também dicas de como escrever testes automatizados que verificam o comportamento da aplicação em um ambiente clusterizado.\nA talk seguinte foi sobre Ash, um framework Elixir que vem começando a ganhar tração na comunidade. Alguns já haviam me falado do Ash, mas ainda não tive a oportunidade de experimentá-lo. A palestra de Zach Daniels, o criador do Ash, apresentou o progresso do trabalho feito até o momento, e as features que estão por vir. O Ash tem como objetivo facilitar a construção de aplicações centradas no domínio (domain). Seu slogan é \u0026ldquo;Model your domain, derive the rest\u0026rdquo;. Os trechos de código no site oficial me lembraram bibliotecas Ruby como Grape e Sinatra. Tenho a impressão de que o Ash permite fazer muita coisa com relativamente pouco código escrito, e estou ansioso para testar a sua proposta em alguns dos meus projetos pessoais. A minha primeira experiência no site deles já me deixou com uma boa impressão — tiveram a sacada de criar um tutorial interativo utilizando LiveBook!\nAndrew Bennett do Whatsapp mostrou algumas ciladas comuns em um ambiente clusterizado, e que podem causar falhas irrecuperáveis no sistema. Em se tratando de um serviço como o Whatsapp, com ~2 bilhões de usuários ativos e clusters com ~30,000 nodes, tais falhas podem causar uma catástrofe! Andrew apresentou o projeto erldist_filter que permite logar e filtrar mensagens trocadas em um ambiente BEAM clusterizado, e como isso vem ajudando o Whatsapp a se proteger de acidentes e evitar downtime.\nEm seguida assisti a palestra do German Velasco. É um prazer aprender com o German! Venho acompanhando seu conteúdo faz algum tempo, e tive a oportunidade de finalmente conhecê-lo pessoalmente antes de sua palestra. German falou sobre o polêmico assunto de \u0026ldquo;Contexts\u0026rdquo;, e como aplicar conceitos de Domain Driven Design para melhorar nosso código.\nAo final do segundo dia tivemos as lighting talks ⚡️ Uma série de apresentações curtas porém não menos interessantes. Não lembro de todas elas, porém gostaria de mencionar a do Paulo Valente, mantenedor do Nx. Paulo mostrou como foi construído o ElixirConf Chess utilizando LiveView Native e Nx.\nDia 3 Uma das minhas palestras favoritas do terceiro dia foi a do Chris Gregori. Ele apresentou diferentes maneiras de trabalhar com tarefas assíncronas em LiveView. Sua talk mencionou algumas das estratégias previamente apresentadas pelo Chris McCord em sua keynote e foi além, incluindo também técnicas que levam em consideração a experiência dos usuários (UX).\nJosé Valim fechou o evento com uma keynote falando sobre o sistema de tipagem estática que está sendo incorporado em Elixir. Este trabalho é o resultado de um estudo feito por ele, Giuseppe Castagna e Guillaume Duboc, e que está passando da fase de pesquisa para a fase de implementação. José explicou porque o sistema de tipos para Elixir não pode simplesmente ser trazido de outras linguagens devido a natureza única da BEAM.\nUm momento delicado de sua keynote foi quando José mencionou o risco de que a adição de tipagem estática na linguagem cause uma divisão na comunidade, entre aqueles que adotam e aqueles que rejeitam.\nComo fã de linguagens estaticamente tipadas como Golang, Rust e Haskell, estou muito empolgado com a possibilidade de adotar mais uma ferramenta que pode auxiliar na modelagem do domínio e de regras de negócio, contando com mais verificações em tempo de compilação.\nConclusão Participar da ElixirConf US 2023 foi uma experiência extremamente enriquecedora. Mais do que as palestras, foi uma chance de interação e conexão com a comunidade — o chamado \u0026ldquo;hallway track\u0026rdquo;.\nPosso dizer que saí da conferência muito empolgado com o futuro da tecnologia e ainda mais inspirado em continuar construindo soluções incríveis em Elixir na Idopter Labs, e que apoiem e impactem positivamente empresas, empreendedores e seus clientes.\nPublicado originalmente na Idopter Labs.\n","permalink":"https://csouza.me/posts/elixirconf-us-2023/","summary":"\u003cp\u003eEm setembro, Orlando voltou a ser palco da ElixirConf US após 7 anos desde sua última edição na cidade. Tive o privilégio de participar novamente desse encontro de três dias, repleto de palestras sobre variados temas relacionados ao ecossistema Elixir e BEAM, organizadas em três trilhas simultâneas. A qualidade das palestras foi tão alta que se tornou um desafio decidir qual assistir! A seguir, faço um resumo das que tive a oportunidade de acompanhar.\u003c/p\u003e","title":"ElixirConf US 2023"},{"content":"In this post we\u0026rsquo;ll learn one the most simple approaches to interacting with the Cardano blockchain using the command line. The command line is the lowest level for interacting with Cardano before using one of its programming language libraries.\nWith the cardano command line, we can send commands directly to a cardano node using the cardano-cli on Linux, Mac or Windows.\nThe Cardano node The Cardano node is the most important and fundamental element of the network.\nThe node communicates with other nodes.\nThe blockchain is a network of nodes which communicate with each other and execute different roles, such as:\n1- Running the protocol.\n2- Validating and propagating blocks.\n3- Producing blocks (just a particular subset of nodes do this)\n4- Executing network queries.\n\u0026ldquo;The blockchain is a network of nodes which communicate with each other and execute different roles\u0026rdquo;\nSince each node has a copy of the blockchain, they can return queries 🔍\nThere are a few different ways to run a node. Those with systems administration and IT knowledge can follow the manual process. This is certainly the most recommended way to build real-world applications such as the backend to a dApp or to operate a Stake Pool.\nOn the other hand, there is a way that\u0026rsquo;s much more approachable and practical, and particularly useful for running tests and basic operations: using the Daedalus desktop wallet.\nThe Daelalus wallet is a project by IOG, one of the companies behind the development of Cardano. It is one of the most secure and one of the easiest wallets to install and use. This wallet includes a dedicated blockchain node, allowing each individual who uses the wallet to also participate in the Cardano network through the aforementioned roles.\nIn fact, referring to Daedalus simply as a wallet is an understatement. In the words of Charles Hoskinson himself,\n\u0026ldquo;Daedalus is a lot of software. It\u0026rsquo;s not a wallet. It\u0026rsquo;s a server, it\u0026rsquo;s a database. Daedalus is the entire cardano protocol.\u0026rdquo;\nWhen installed and run for the first time, Daedalus will need to sync the entire blockchain history since its first block. This can take a few hours ⏳\nOnly once the sync is complete, will the wallet be available for use.\nFor now, let\u0026rsquo;s skip the commands to use the wallet and jump straight to the command line 💪 The Terminal.app is used to run the commands.\nCommunicating with the Testnet Before commands can be run against the Cardano node, the following environment variable needs to be set.\nCARDANO_NODE_SOCKET_PATH\nAs the name implies, this environment variable points to the socket used to communicate with the node. To communicate with \u0026ldquo;Testnet\u0026rdquo;, confirm that the following file,\ncardano-node.socket\nexists on the following directory,\n~/Library/Application\\ Support/Daedalus\\ Testnet/\nRun this command to populate the environment variable:\nexport CARDANO_NODE_SOCKET_PATH=~/Library/Application\\ Support/Daedalus\\ Testnet/cardano-node.socket Make sure the value is properly set:\necho $CARDANO_NODE_SOCKET_PATH Switch folders to where Daedalus stores the cardano-cli and other executables for the testnet. Run the following:\ncd /Applications/Daedalus\\ Testnet.app/Contents/MacOS/ Inside the new folder, look for cardano-cli.\nThis is the program used to interact with the node. From this folder, run the following command to query real-time information from the blockchain:\n./cardano-cli query tip --testnet-magic 1097911063 The value for the testnet magic is read from this file. This parameter is used by cryptographic functions implemented as part of the protocol and differentiates communication between test and main network.\nThe query tip command returns a summarized snapshot of the current state of the network as known by the current node. One example of result is the following JSON:\n{ \u0026#34;era\u0026#34;: \u0026#34;Alonzo\u0026#34;, \u0026#34;epoch\u0026#34;: 185, \u0026#34;hash\u0026#34;: \u0026#34;886250a606c2d9968c5b1508e56cc2cd79f5abf99cb848695e8ecfb84c7b60dc\u0026#34;, \u0026#34;block\u0026#34;: 3299613, \u0026#34;slot\u0026#34;: 49830257, \u0026#34;syncProgress\u0026#34;: \u0026#34;100.00\u0026#34; } Try running this command yourself and if you see a result similar to this one, congratulations! You have officially interacted with Cardano Testnet! 👏\nNow let\u0026rsquo;s see how to interact with the main network.\nCommunicating with Mainnet Check for cardano-node.socket on the following directory:\n~/Library/Application\\ Support/Daedalus\\ Mainnet/\nRun this command to populate the environment variable for mainnet:\nexport CARDANO_NODE_SOCKET_PATH=~/Library/Application\\ Support/Daedalus\\ Mainnet/cardano-node.socket Make sure the value is properly set:\necho $CARDANO_NODE_SOCKET_PATH Change directories to where Daedalus stores the cardano-cli and other executables for the main network with the following command:\ncd /Applications/Daedalus\\ Mainnet.app/Contents/MacOS/ Inside this folder, look for cardano-cli.\nRun the following command:\n./cardano-cli query tip --mainnet Note the option --mainnet instead of --testnet-magic. With this option, cardano-cli commands run on the main network and no longer on the test network. One example result is the following JSON:\n{ \u0026#34;era\u0026#34;: \u0026#34;Alonzo\u0026#34;, \u0026#34;epoch\u0026#34;: 319, \u0026#34;hash\u0026#34;: \u0026#34;869ed5d7788e5518ef7195c3731a2d37b9bd2e861820b377ef87f6f062edf640\u0026#34;, \u0026#34;block\u0026#34;: 6852938, \u0026#34;slot\u0026#34;: 52675349, \u0026#34;syncProgress\u0026#34;: \u0026#34;100.00\u0026#34; } You have now interacted with Cardano Mainnet! 👏\nThe query tip command is one of the many available commands. One could say it is just the tip… of the iceberg\nA comprehensive list of commands to create wallets, manage transactions, operate stake pools and publish smart contracts to the network can be found on the official documentation for the cardano-cli.\nConclusion A simple and effective way to run a node and interact with the Cardano blockchain is through Daedalus. The Daedalus wallet is a Desktop wallet that includes its own node of the blockchain. This allows individual users to participate on the network and run commands using the cardano-cli.\nTo learn more about Cardano, checkout https://workwithcardano.com/ and follow us on Twitter.com/workwithcardano.\nOriginally published on Medium.\n","permalink":"https://csouza.me/posts/interact-with-cardano-blockchain-using-the-command-line/","summary":"\u003cp\u003eIn this post we\u0026rsquo;ll learn one the most simple approaches to interacting with the Cardano blockchain using the command line. The command line is the lowest level for interacting with Cardano before using one of its \u003ca href=\"https://github.com/input-output-hk/cardano-node/tree/master/cardano-cli\"\u003eprogramming language libraries\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eWith the cardano command line, we can send commands directly to a cardano \u003cstrong\u003enode\u003c/strong\u003e using the \u003cstrong\u003ecardano-cli\u003c/strong\u003e on Linux, Mac or Windows.\u003c/p\u003e\n\u003ch2 id=\"the-cardano-node\"\u003eThe Cardano node\u003c/h2\u003e\n\u003cp\u003eThe Cardano \u003cstrong\u003enode\u003c/strong\u003e is the most important and fundamental element of the network.\u003c/p\u003e","title":"The most simple way to interact with the Cardano blockchain using the command line"},{"content":"Esse artigo é um resumo da conversa que tive com meu amigo Zeca no episódio número 15 do Espresso Tech. Nesse episódio, demos 5 dicas para aqueles que desejam buscar um emprego na área de tecnologia 👊🏻\nEssas dicas foram selecionadas baseadas na nossa experiência atuando na área de tecnologia há mais de 15 anos — tanto no papel de empregado em busca de novas oportunidades e novos projetos, quanto no papel de empregador avaliando candidatos e contratando novos membros para a nossa equipe na Idopter Labs.\n* Estamos contratando developers Elixir e React Native! Entre em contato conosco em contato@idopterlabs.com.br caso você queira construir aplicações de software incríveis que apoiem e impactem positivamente as pessoas.\nAs 5 dicas para você conseguir um emprego na área de tecnologia são as seguintes:\nEscolha uma tecnologia de acordo com os problemas que você gosta de resolver. Busque conhecimento antes que surja a oportunidade. Crie uma boa reputação na comunidade — chegue ajudando. Pesquise empresas e entre em contato com pessoas que já trabalham lá. Monte um currículo atualizado e de acordo com as vagas. Abaixo, uma breve elaboração sobre cada dica. Caso você tenha interesse em ouvir mais detalhes, recomendo demais escutar ao episódio completo 🎙 no link a seguir: https://biolinky.co/espressotech\n1. Escolha uma tecnologia de acordo com os problemas que você gosta de resolver A área de tecnologia possui diversas áreas de especialidade. Backend, front-end, mobile, DevOps, gerenciamento de projetos, gerenciamento de produtos e por aí vai. Cada uma dessas especialidades exige um leque de conhecimentos distinto. Diferentes linguagens, práticas, frameworks, ferramentas, serviços, particularidades.\nPor exemplo, é impossível trabalhar com front-end e não conhecer JavaScript. Mesmo que você prefira escrever código em outros \u0026ldquo;sabores\u0026rdquo; de JavaScript como TypeScript, CoffeeScript, PureScript ou ClojureScript, o conhecimento de JavaScript é imprescindível para soluções que envolvem interações com um browser.\nDa mesma forma, é praticamente impossível trabalhar com DevOps em projetos modernos sem conhecer containers. Ferramentas como Docker e Kubernetes ajudam no processo de deployment e possibilitam uma maior portabilidade das aplicações.\nInterações com o browser e deployment de aplicações são apenas dois dos inúmeros desafios que existem na área de tecnologia.\nIdentifique os desafios que você mais gosta e, a partir deles, observe quais tecnologias são utilizadas para sua solução. Invista algum tempo avaliando essas tecnologias e eleja uma para especializar-se.\n2. Busque o conhecimento antes que surja a oportunidade \u0026ldquo;Para ganhar algum conhecimento em tecnologia, não precisa esperar por um emprego.\u0026rdquo;\nUma das vantagens de atuar na área de tecnologia é o fácil acesso a materiais de aprendizado, ferramentas e infraestrutura. Uma pessoa auto-didata e pró-ativa possui a capacidade de buscar conhecimento relacionado a alguma de suas tecnologias de escolha antes que surja alguma oportunidade de ser paga por isso.\nÀs vezes a oportunidade surge primeiro\u0026hellip; Ótimo! Aprendizado sob demanda funciona também.\nPorém, caso a curiosidade estiver de acordo com a recompensa, mesmo que apenas intelectual, vá em frente e estude! Compre livros, faça cursos, leia artigos, assista a vídeos.\nNa área de tecnologia, o conhecimento está ao alcance de todos.\n3. Crie uma boa reputação na comunidade Give, give, give. — https://smallactsmanifesto.org/\nA melhor forma de criar uma boa reputação na comunidade é ajudando a comunidade. Envolva-se em grupos, escreva artigos e ajude as pessoas da sua comunidade através do compartilhamento de conhecimento 🧠\nNão precisa ser especialista para ensinar, basta estar um pouco a frente de quem queira aprender.\nUma boa reputação pode ajudar em conversas iniciais, e acelerar a passagem por fases preliminares.\n4. Pesquise empresas e entre em contato Certamente já existem empresas que atuam na área dos problemas que você gosta de resolver. Selecione algumas que se destacam para você e entre em contato. Procure saber como é trabalhar lá, quais os tipos de projetos e problemas o time gosta de resolver.\nMesmo que as vagas pareçam acima do seu conhecimento atual, entre em contato! Ouça do próprio pessoal da empresa o quanto de experiência é necessário.\nSe essa empresa não existe.. ótimo! Você descobriu uma oportunidade 💡\nMas há grandes chances de alguma empresa com projetos e cultura alinhados com a sua já existir e você pode entrar em contato com elas.\n5. Monte um currículo de acordo com a vaga Saber um pouco sobre tudo, mesmo que superficialmente, não atrapalha. Acredito que em um mundo repleto de pessoas altamente especializadas, os generalistas triunfam. O livro Range, do David Epstein, fala exatamente sobre esse assunto. Recomendo demais a leitura!\nEntretanto, vagas em tecnologia normalmente procuram especialistas 🤺\nPara vagas que buscam profissionais para uma tecnologia específica, mencione primeiramente o que você sabe sobre essa tecnologia. Seu currículo deve ser breve e direto ao ponto.\nTecnologias complementares são importantes mas provavelmente existe uma tecnologia que supre a necessidade imediata da vaga e te leva direto a conseguir o emprego. Mira e só vai 🎯\nConclusão Essas são as 5 dicas. Todas foram comprovadas como sendo eficazes para conseguir um emprego na área de tecnologia por um grande número de pessoas — incluindo eu — mais de uma vez, tanto no papel de empregado quanto no de empregador.\nEscute ao episódio para ouvir algumas histórias bem interessantes envolvendo essas dicas e envie-nos um e-mail em contato@idopterlabs.com.br caso tenha interesse em trabalhar em projetos Elixir ou React Native. No e-mail, inclua referências suas relacionadas a essas tecnologias 😉 e um pouco sobre você.\nEspero ter ajudado!\nPublicado originalmente na Idopter Labs.\n","permalink":"https://csouza.me/posts/dicas-emprego-na-area-de-tecnologia/","summary":"\u003cp\u003eEsse artigo é um resumo da conversa que tive com meu amigo Zeca no episódio número 15 do \u003ca href=\"https://biolinky.co/espressotech\"\u003e\u003cstrong\u003eEspresso Tech\u003c/strong\u003e\u003c/a\u003e. Nesse episódio, demos 5 dicas para aqueles que desejam buscar um emprego na área de tecnologia 👊🏻\u003c/p\u003e\n\u003cp\u003eEssas dicas foram selecionadas baseadas na nossa experiência atuando na área de tecnologia há mais de 15 anos — tanto no papel de empregado em busca de novas oportunidades e novos projetos, quanto no papel de empregador avaliando candidatos e contratando novos membros para a nossa equipe na \u003ca href=\"https://www.idopterlabs.com.br/\"\u003eIdopter Labs\u003c/a\u003e.\u003c/p\u003e","title":"Dicas para conseguir um emprego na área de tecnologia"},{"content":"A utilização de selects dinâmicos é uma excelente forma de filtrar uma listagem muito grande e tornar a navegação de uma aplicação mais amigável.\nNeste post iremos construir selects dinâmicos com Phoenix e LiveView. A aplicação de exemplo é uma simples página de listagem de guitarras.\n(O segundo select é preenchido dinamicamente, de acordo com o valor do primeiro)\nUma guitarra pertence a uma marca, como Gibson ou Fender. Cada marca oferece algumas opções diferentes de modelo, como Les Paul e SG no caso da Gibson, ou Stratocaster e Telecaster no caso da Fender.\nDiante da página de listagem de guitarras, os usuários podem selecionar a marca no primeiro select. De acordo com a marca selecionada, os modelos pertencentes a marca são populados no segundo select.\nRenderizando a página O primeiro passo é a criação da rota que associa uma URL a uma LiveView. Neste exemplo, associamos a URL raíz / ao módulo GuitarLive.Index através da seguinte definição no arquivo router.ex:\nscope \u0026#34;/\u0026#34;, InstrumentStoreSelectWeb do pipe_through(:browser) live(\u0026#34;/\u0026#34;, GuitarLive.Index, :index) end O módulo GuitarLive.Index é responsável pelas definições das callback functions executadas de acordo com o ciclo de vida da LiveView e interações dos usuários com a página. As callback functions são mount/3 e handle_event/3.\nA função mount/3 é responsável pelos dados iniciais que serão exibidos na página.\ndef mount(_params, _session, socket) do brands = Inventory.list_unique_brands() {:ok, socket |\u0026gt; assign(:guitars, []) |\u0026gt; assign(:models, []) |\u0026gt; assign(:selected_brand, nil) |\u0026gt; assign(:brands, brands)} end Nessa função, fazemos uma chamada à função list_unique_brands/0, parte do módulo Inventory. Esse módulo é o context module, responsável por encapsular os detalhes de acesso ao banco de dados. A função list_unique_brands/0 retorna uma lista de marcas que será usada para popular o primeiro select no template index.html.heex a seguir:\n\u0026lt;form phx-change=\u0026#34;set-filter\u0026#34;\u0026gt; \u0026lt;select name=\u0026#34;brand\u0026#34; id=\u0026#34;brand\u0026#34;\u0026gt; \u0026lt;option/\u0026gt; \u0026lt;%= for brand \u0026lt;- @brands do %\u0026gt; \u0026lt;option value={brand} selected={if @selected_brand == brand, do: \u0026#34;selected\u0026#34;}\u0026gt; \u0026lt;%= brand %\u0026gt; \u0026lt;/option\u0026gt; \u0026lt;% end %\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/form\u0026gt; Respondendo a eventos do DOM Uma das formas mais simples de responder a eventos do DOM em LiveView é através de bindings. O binding phx-change é responsável por \u0026ldquo;ligar\u0026rdquo; qualquer evento de mudança no formulário a função handle_event/3, definida no módulo GuitarLive.Index.\nDe volta a GuitarLive.Index, implementamos a primeira versão dessa função:\ndef handle_event(\u0026#34;set-filter\u0026#34;, %{\u0026#34;_target\u0026#34; =\u0026gt; [\u0026#34;brand\u0026#34;], \u0026#34;brand\u0026#34; =\u0026gt; brand}, socket) do {:noreply, socket |\u0026gt; assign(:models, Inventory.filter_models_by_brand(brand)) |\u0026gt; assign(:selected_brand, brand)} end Utilizamos pattern matching para interceptar o evento (\u0026quot;set-filter\u0026quot;), os dados da origem do evento (\u0026quot;_target\u0026quot; =\u0026gt; [\u0026quot;brand\u0026quot;]), valor selecionado no primeiro select (\u0026quot;brand\u0026quot; =\u0026gt; brand), e o socket referente ao usuário interagindo com a página (socket).\nUma vez selecionada a marca, a função handle_event/3 irá filtrar os modelos disponíveis desta marca através da função filter_models_by_brand/1 do módulo Inventory. Os modelos de guitarra estarão disponíveis no socket através da propriedade models, que pode ser acessada no template através de @models:\n\u0026lt;select name=\u0026#34;model\u0026#34; id=\u0026#34;model\u0026#34;\u0026gt; \u0026lt;option/\u0026gt; \u0026lt;%= for model \u0026lt;- @models do %\u0026gt; \u0026lt;option value={model}\u0026gt;\u0026lt;%= model %\u0026gt;\u0026lt;/option\u0026gt; \u0026lt;% end %\u0026gt; \u0026lt;/select\u0026gt; No que diz respeito a selects dinâmicos, a feature está concluída! Ao detectar mudanças no primeiro select, a função handle_event/3 será executada pelo Phoenix e irá popular o socket com os dados a serem utilizados pelo segundo select 💥\nListando as guitarras A última etapa é a listagem das guitarras, filtradas por marca e modelo. Para isso, definimos uma nova clause da função handle_event/3 conforme o código a seguir:\ndef handle_event(\u0026#34;set-filter\u0026#34;, %{\u0026#34;_target\u0026#34; =\u0026gt; [\u0026#34;model\u0026#34;], \u0026#34;model\u0026#34; =\u0026gt; model}, socket) do selected_brand = socket.assigns.selected_brand guitars = Inventory.filter_by_brand_and_model(selected_brand, model) {:noreply, assign(socket, :guitars, guitars)} end A assinatura dessa função é bem parecida com a anterior, mas não é exatamente igual. Note a diferença no segundo argumento:\n%{\u0026quot;_target\u0026quot; =\u0026gt; [\u0026quot;brand\u0026quot;], \u0026quot;brand\u0026quot; =\u0026gt; brand}\nvs.\n%{\u0026quot;_target\u0026quot; =\u0026gt; [\u0026quot;model\u0026quot;], \u0026quot;model\u0026quot; =\u0026gt; model}\nAmbos os elementos \u0026lt;select\u0026gt; emitem o mesmo evento \u0026quot;set-filter\u0026quot;, porém, o primeiro emite valores para brand enquanto o segundo emite valores para model.\nUma vez populada com os valores das guitarras a serem exibidas, a socket permite que o template leia esses valores e monte a tabela:\n\u0026lt;tbody id=\u0026#34;guitars\u0026#34;\u0026gt; \u0026lt;%= for guitar \u0026lt;- @guitars do %\u0026gt; \u0026lt;tr id={\u0026#34;guitar-#{guitar.id}\u0026#34;}\u0026gt; \u0026lt;td\u0026gt;\u0026lt;%= guitar.brand %\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;\u0026lt;%= guitar.model %\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;\u0026lt;%= guitar.year %\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;% end %\u0026gt; \u0026lt;/tbody\u0026gt; A listagem está concluída!\nConclusão A utilização de selects dinâmicos é uma excelente forma de filtrar uma listagem muito grande e tornar a navegação mais amigável. Aprendemos como implementar uma forma de select dinâmico com Phoenix e LiveView, através de bindings e callback functions.\nPara os interessados em explorar o código fonte da aplicação de exemplo mais a fundo, ele está disponível no link a seguir:\nGitHub - idopterlabs/InstrumentStoreSelect\nEspero que este post ajude você em seu próximo projeto. Caso sua empresa precise de ajuda na construção de aplicações Elixir e LiveView, entre em contato!\nPublicado originalmente na Idopter Labs.\n","permalink":"https://csouza.me/posts/selects-dinamicos-com-liveview/","summary":"\u003cp\u003eA utilização de selects dinâmicos é uma excelente forma de filtrar uma listagem muito grande e tornar a navegação de uma aplicação mais amigável.\u003c/p\u003e\n\u003cp\u003eNeste post iremos construir selects dinâmicos com \u003cem\u003ePhoenix\u003c/em\u003e e \u003cem\u003eLiveView\u003c/em\u003e. A aplicação de exemplo é uma simples página de listagem de guitarras.\u003c/p\u003e\n\u003cp\u003e(O segundo select é preenchido dinamicamente, de acordo com o valor do primeiro)\u003c/p\u003e\n\u003cp\u003eUma guitarra pertence a uma marca, como Gibson ou Fender. Cada marca oferece algumas opções diferentes de modelo, como Les Paul e SG no caso da Gibson, ou Stratocaster e Telecaster no caso da Fender.\u003c/p\u003e","title":"Selects dinâmicos com LiveView"},{"content":"Neste post aprenderemos uma das formas mais simples de interação com a blockchain da Cardano através da cardano-cli no Mac OS.\nA comunicação com a blockchain da Cardano pela linha de comando é feita através de interações com um nó, ou \u0026ldquo;node\u0026rdquo;.\nUm nó é o elemento fundamental e mais importante da rede.\nA blockchain é uma rede de nós que comunicam-se entre si e executam diferentes papéis, como:\n1- Execução do protocolo.\n2- Validação e propagação de blocos.\n3- Produção de blocos (apenas alguns dos nós fazem isso).\n4- Execução de consultas na rede.\nA blockchain é uma rede de nós que comunicam-se entre si e executam diferentes papéis.\nUma vez que os nós possuem uma cópia da blockchain, eles também podem ser utilizados para executar consultas.\nExistem algumas formas diferentes de rodar um nó. Aqueles com conhecimento de TI e administração de sistemas podem seguir o processo manual. Esta é certamente a forma mais recomendada caso o objetivo seja a construção de sistemas importantes, tais como o backend de uma dApp ou o gerenciamento de uma Stake Pool. Por outro lado, existe uma forma muito mais simples e \u0026ldquo;low-barrier\u0026rdquo; de rodar um nó da Cardano caso o objetivo seja estudar as operações básicas. Esta forma é através da utilização da carteira (\u0026ldquo;wallet\u0026rdquo;) Daedalus.\nA Daedalus, projeto da Input Output, a principal patrocinadora da Cardano, é uma das carteiras mais seguras e também uma das mais fáceis de instalar. Ela é uma carteira desktop que inclui seu próprio nó da blockchain da Cardano. Isso possibilita que seus usuários participem da rede, exercendo os papéis previamente mencionados.\nAo ser utilizada pela primeira vez, a Daedalus precisará baixar a blockchain inteira da Cardano desde o primeiro bloco produzido. Isso pode levar algumas horas ⏳\nUma fez concluída a sincronização do nó da Daedalus com a rede, a carteira estará pronta para utilização.\nEste post não entrará em detalhes de como realizar operações com a carteira. Vamos pular esta parte e partir direto para a linha de comando 💪 Para isso, executaremos comandos no Terminal.app.\n🚨 Atenção: os comandos a seguir funcionarão apenas quando a carteira estiver completamente sincronizada. 🚨\nComunicando-se com a Testnet Antes que seja possível a comunicação com o nó, é preciso configurar a variável de ambiente CARDANO_NODE_SOCKET_PATH. O valor desta variável será o caminho do socket utilizado pelo nó. Para a rede de testes, ou \u0026ldquo;Testnet\u0026rdquo;, certifique-se da presença do arquivo cardano-node.socket no diretório a seguir:\n~/Library/Application\\ Support/Daedalus\\ Testnet/\nUma vez confirmada a localização deste arquivo, execute o comando a seguir para popular a variável de ambiente:\nexport CARDANO_NODE_SOCKET_PATH=~/Library/Application\\ Support/Daedalus\\ Testnet/cardano-node.socket Para confirmar que o valor foi corretamente atribuído, rode o comando a seguir e verifique que o resultado retornado é a localização do arquivo de socket:\necho $CARDANO_NODE_SOCKET_PATH Agora vamos mudar para o diretório onde a Daedalus guarda a cardano-cli e outros executáveis da rede de testes. Rode o comando a seguir:\ncd /Applications/Daedalus\\ Testnet.app/Contents/MacOS/ Dentro deste diretório, verifique a presença da cardano-cli.\nEste é o programa permitirá a interação com o nó. A partir desse diretório, execute o seguinte comando que consulta as informações mais recentes da blockchain:\n./cardano-cli query tip --testnet-magic 1097911063 (O valor para testnet-magic é obtido através deste arquivo. Este parâmetro é utilizado por funções criptográficas internas ao protocolo, e diferencia a comunicação com a rede de testes da comunicação com a rede principal.)\nUm exemplo de resultado retornado é o JSON a seguir:\n{ \u0026#34;era\u0026#34;: \u0026#34;Alonzo\u0026#34;, \u0026#34;epoch\u0026#34;: 185, \u0026#34;hash\u0026#34;: \u0026#34;886250a606c2d9968c5b1508e56cc2cd79f5abf99cb848695e8ecfb84c7b60dc\u0026#34;, \u0026#34;block\u0026#34;: 3299613, \u0026#34;slot\u0026#34;: 49830257, \u0026#34;syncProgress\u0026#34;: \u0026#34;100.00\u0026#34; } Caso o seu comando tenha retornado um resultado parecido com esse, parabéns! Você interagiu com a rede de testes da Cardano através da linha de comando 👏 Agora vamos aprender como fazer o mesmo na rede principal.\nComunicando-se com a Mainnet Os passos para comunicação com a rede principal, ou \u0026ldquo;Mainnet\u0026rdquo;, são muito parecidos com aqueles para a comunicação com a rede de testes.\nCertifique-se da presença do arquivo cardano-node.socket no diretório a seguir:\n~/Library/Application\\ Support/Daedalus\\ Mainnet/\nExecute o comando a seguir para popular a variável de ambiente:\nexport CARDANO_NODE_SOCKET_PATH=~/Library/Application\\ Support/Daedalus\\ Mainnet/cardano-node.socket Rode o comando a seguir, e verifique que o resultado retornado é a localização do arquivo de socket da rede principal:\necho $CARDANO_NODE_SOCKET_PATH Agora mude para o diretório aonde a Daedalus guarda a cardano-cli e outros executáveis da rede principal com o seguinte comando:\ncd /Applications/Daedalus\\ Mainnet.app/Contents/MacOS/ Dentro deste diretório, verifique a presença da cardano-cli.\nA partir deste diretório, execute o seguinte comando para consultar as informações mais recentes da blockchain:\n./cardano-cli query tip --mainnet Note a utilização da opção --mainnet ao invés de --testnet-magic. Com esta opção, os comandos da cardano-cli são executados na rede principal, e não mais na rede de testes.\nUm exemplo de resultado para esta consulta na rede principal é o JSON a seguir:\n{ \u0026#34;era\u0026#34;: \u0026#34;Alonzo\u0026#34;, \u0026#34;epoch\u0026#34;: 319, \u0026#34;hash\u0026#34;: \u0026#34;869ed5d7788e5518ef7195c3731a2d37b9bd2e861820b377ef87f6f062edf640\u0026#34;, \u0026#34;block\u0026#34;: 6852938, \u0026#34;slot\u0026#34;: 52675349, \u0026#34;syncProgress\u0026#34;: \u0026#34;100.00\u0026#34; } Caso o seu comando tenha retornado um resultado parecido com esse, parabéns! Você interagiu com a rede principal da Cardano através da linha de comando 🤘🏻\nO comando query tip é apenas um dos vários comandos disponíveis. Para aprender sobre os outros comandos, incluindo comandos para criar carteiras, realizar transações, operar stake pools, publicar smart contracts na rede e mais, consulte a documentação de referência da cardano-cli.\nConclusão Uma forma muito simples e de rodar um nó e interagir com a blockchain da Cardano é através da utilização da Daedalus. A Daedalus é uma carteira desktop que inclui seu próprio nó da blockchain. Isso possibilita que seus usuários participem da rede e executem comandos através da cardano-cli.\nNeste post aprendemos como rodar comandos na rede de testes, \u0026ldquo;Testnet\u0026rdquo;, e rede principal, \u0026ldquo;Mainnet\u0026rdquo;, utilizando a cardano-cli.\nGostou desse artigo e gostaria aprender mais sobre Cardano? Deixe seu comentário com sugestões ou então fale comigo no twitter!\nPublicado originalmente no Medium.\n","permalink":"https://csouza.me/posts/interacao-mais-simples-com-blockchain-cardano/","summary":"\u003cp\u003eNeste post aprenderemos uma das formas mais simples de interação com a blockchain da Cardano através da \u003ccode\u003ecardano-cli\u003c/code\u003e no Mac OS.\u003c/p\u003e\n\u003cp\u003eA comunicação com a blockchain da Cardano pela linha de comando é feita através de interações com um nó, ou \u0026ldquo;node\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eUm nó é o elemento fundamental e mais importante da rede.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eA blockchain é uma rede de nós\u003c/strong\u003e que \u003ca href=\"https://en.wikipedia.org/wiki/Peer-to-peer\"\u003ecomunicam-se entre si\u003c/a\u003e e executam diferentes papéis, como:\u003c/p\u003e\n\u003cp\u003e1- Execução do \u003ca href=\"https://cardano.org/ouroboros/\"\u003eprotocolo\u003c/a\u003e.\u003c/p\u003e","title":"A interação mais simples com a blockchain da Cardano via linha de comando"},{"content":"Neste post iremos aprender como utilizar a AWS CLI para recuperar objetos deletados em um bucket S3.\nAtivação de Versionamento de Objetos A recuperação de objetos deletados é possível 🚨 apenas em buckets com versionamento habilitado 🚨. Por padrão, ao criarmos novos buckets, a funcionalidade de versionamento não é habilitada automaticamente.\nPara verificar se um bucket S3 possui versionamento, podemos executar o comando a seguir:\naws s3api get-bucket-versioning --bucket guitar-store Neste exemplo, o comando irá retornar o status de versionamento do bucket guitar-store. Um retorno vazio significa que o versionamento não está habilitado.\nPara habilitar o versionamento do bucket, podemos executar o seguinte comando:\naws s3api put-bucket-versioning \\ --bucket guitar-store \\ --versioning-configuration Status=Enabled Este comando não deve retornar nada. Ao rodarmos novamente o comando get-bucket-versioning, o seguinte resultado deve ser retornado:\n{ \u0026#34;Status\u0026#34;: \u0026#34;Enabled\u0026#34; } Este resultado indica que o versionamento está habilitado 🙌. A partir deste momento, caso sejam deletados, objetos do bucket podem ser recuperados.\nListando DeleteMarkers Quando um objeto é deletado de um bucket versionado, um DeleteMarker é criado para este objeto. O DeleteMarker é o que determina que um objeto foi deletado. O comando a seguir lista todos os objetos deletados do nosso bucket guitar-store:\naws s3api list-object-versions --bucket guitar-store \\ --query \u0026#39;DeleteMarkers[?IsLatest==`true`]\u0026#39; Caso nenhum objeto tenha sido deletado, o comando retorna o valor null.\nÉ possível filtrar objetos de uma pasta específica com a opção --prefix, como no exemplo a seguir:\naws s3api list-object-versions --bucket guitar-store \\ --prefix \u0026#39;guitar-specs\u0026#39; \\ --query \u0026#39;DeleteMarkers[?IsLatest==`true`]\u0026#39; Este comando retorna uma lista de DeleteMarkers referentes a objetos da pasta guitar-specs. A seguir, um exemplo de resultado incluindo um objeto deletado:\n[ { \u0026#34;Owner\u0026#34;: { \u0026#34;DisplayName\u0026#34;: \u0026#34;...\u0026#34;, \u0026#34;ID\u0026#34;: \u0026#34;5abf415843b8b9696ed2a9fb5f283ef0759e3134\u0026#34; }, \u0026#34;Key\u0026#34;: \u0026#34;guitar-specs/guitar-4.pdf\u0026#34;, \u0026#34;VersionId\u0026#34;: \u0026#34;xq7BLXappOIJowZq9pgTPAe.Bric72yk\u0026#34;, \u0026#34;IsLatest\u0026#34;: true, \u0026#34;LastModified\u0026#34;: \u0026#34;2021-12-14T16:30:12+00:00\u0026#34; } ] Este resultado indica que o objeto guitar-specs/guitar-4.pdf foi deletado do bucket na data e hora indicada em LastModified.\nDeletando DeleteMarkers A partir de informações retornadas na lista de DeleteMarkers, é possível recuperar cada um dos objetos deletados. Para cada objeto que desejamos recuperar, precisamos dos atributos Key e VersionId. No exemplo anterior, estes valores são:\nguitar-specs/guitar-4.pdf xq7BLXappOIJowZq9pgTPAe.Bric72yk Com estes valores, executamos o comando a seguir:\naws s3api delete-object --bucket guitar-store \\ --key \u0026#34;guitar-specs/guitar-4.pdf\u0026#34; \\ --version-id \u0026#34;xq7BLXappOIJowZq9pgTPAe.Bric72yk\u0026#34; Este comando deve retornar o resultado a seguir:\n{ \u0026#34;DeleteMarker\u0026#34;: true, \u0026#34;VersionId\u0026#34;: \u0026#34;xq7BLXappOIJowZq9pgTPAe.Bric72yk\u0026#34; } Após executado o comando, o objeto deverá encontrar-se novamente no bucket e disponível para acesso — respeitando as mesmas permissões previamente estabelecidas.\nConclusão Neste post aprendemos como recuperar objetos deletados de buckets S3 através da AWS CLI. Para que isto seja possível, antes de mais nada é necessário que o bucket tenha a funcionalidade de versionamento habilitada. Por padrão, esta funcionalidade não é habilitada automaticamente para novos buckets, portanto é necessário (e recomendado!) que isto seja feito.\nAlém do S3, utilizamos vários outros serviços da AWS em nossos projetos na IdopterLabs. Caso precise de ajuda em AWS, Elixir ou React Native para seu projeto, entre em contato conosco! 👉 contato@idopterlabs.com.br\nPublicado originalmente na Idopter Labs.\n","permalink":"https://csouza.me/posts/recuperando-arquivos-deletados-aws-s3/","summary":"\u003cp\u003eNeste post iremos aprender como utilizar a \u003ca href=\"https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html\"\u003eAWS CLI\u003c/a\u003e para recuperar objetos deletados em um bucket S3.\u003c/p\u003e\n\u003ch2 id=\"ativação-de-versionamento-de-objetos\"\u003eAtivação de Versionamento de Objetos\u003c/h2\u003e\n\u003cp\u003eA recuperação de objetos deletados é possível 🚨 \u003cstrong\u003eapenas em buckets com versionamento habilitado\u003c/strong\u003e 🚨. Por padrão, ao criarmos novos buckets, a funcionalidade de versionamento não é habilitada automaticamente.\u003c/p\u003e\n\u003cp\u003ePara verificar se um bucket S3 possui versionamento, podemos executar o comando a seguir:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eaws s3api get-bucket-versioning --bucket guitar-store\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNeste exemplo, o comando irá retornar o status de versionamento do bucket \u003cem\u003eguitar-store\u003c/em\u003e. Um retorno vazio significa que o versionamento \u003cstrong\u003enão está habilitado.\u003c/strong\u003e\u003c/p\u003e","title":"Recuperando Arquivos deletados no AWS S3 através da CLI"}]