<?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[Grimoire by Iriyanto]]></title><description><![CDATA[Grimoire by Iriyanto is a personal tech blog sharing thoughts and lessons on programming, software engineering, and everything around building with code.]]></description><link>https://blog.iriyanto.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1758222804984/7fb4a5a8-e135-472e-a947-5db35d951dd0.png</url><title>Grimoire by Iriyanto</title><link>https://blog.iriyanto.com</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 18:19:01 GMT</lastBuildDate><atom:link href="https://blog.iriyanto.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Designing the Core Architecture]]></title><description><![CDATA[Introduction
After deciding to build my own NestJS StarterKit, the first challenge was to design a core architecture that actually deserves to be called a “starter.” Not just a bunch of boilerplate folders, but a structure that could grow with real-w...]]></description><link>https://blog.iriyanto.com/designing-the-core-architecture</link><guid isPermaLink="true">https://blog.iriyanto.com/designing-the-core-architecture</guid><category><![CDATA[starterkit]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[architecture]]></category><category><![CDATA[software design]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Backend Development]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Tue, 07 Oct 2025 17:00:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759646328222/fbe3eb74-90c1-4e6b-a1a6-74c31c7d35dc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>After deciding to build my own NestJS StarterKit, the first challenge was to design a core architecture that actually <em>deserves</em> to be called a “starter.” Not just a bunch of boilerplate folders, but a structure that could grow with real-world projects, from early MVPs to production-grade systems.</p>
<p>At my last job, I worked at a software house that delivered a lot of client projects. Each new project started with the same conversation:</p>
<blockquote>
<p>“Should we copy the last repo again?”</p>
</blockquote>
<p>That’s the problem I want to solve, to create a starter that’s modular, maintainable, and easy to extend. This article walks through how I designed the core architecture of that StarterKit.</p>
<h2 id="heading-architectural-goals"><strong>Architectural Goals</strong></h2>
<p>Before touching any line of code, I wrote down a few non-negotiables for the architecture:</p>
<ol>
<li><p><strong>Fast to start</strong> — a new MVP should be bootstrapped in minutes.</p>
</li>
<li><p><strong>Modular and scalable</strong> — each domain should live independently, but still play nicely with others.</p>
</li>
<li><p><strong>Developer-friendly</strong> — clear conventions, minimal surprises.</p>
</li>
<li><p><strong>No vendor lock-in</strong> — I want to own the stack, not depend on third-party auth or config systems.</p>
</li>
<li><p><strong>Extendable without refactor hell</strong> — adding a feature shouldn’t break existing modules.</p>
</li>
</ol>
<p>These goals shape every decision in the StarterKit, from folder naming to dependency design.</p>
<h2 id="heading-core-design-principles"><strong>Core Design Principles</strong></h2>
<p>There are three big ideas behind the architecture:</p>
<ul>
<li><p><strong>Domain-driven modular structure</strong><br />  Each domain (like <code>user</code>, <code>auth</code>, <code>post</code>, etc.) lives as its own NestJS module — fully self-contained, with its own entities, DTOs, and services.</p>
</li>
<li><p><strong>Convention over configuration</strong><br />  The StarterKit uses opinionated patterns (like where to put <code>guards</code>, <code>filters</code>, or <code>repositories</code>) so developers spend less time deciding and more time building.</p>
</li>
<li><p><strong>DIY-first, vendor-second</strong><br />  Instead of relying on services like Clerk or Auth0, I implement our own auth, config, and persistence layers, flexible and portable.</p>
</li>
</ul>
<h2 id="heading-folder-amp-module-structure"><strong>Folder &amp; Module Structure</strong></h2>
<p>Here’s the project skeleton at a glance:</p>
<pre><code class="lang-markdown">src/
├── common/
│   ├── dtos/
│   ├── interceptors/
│   ├── helpers/
│   └── ...
├── config/
│   ├── app.config.ts
│   └── ...
├── lib/
│   ├── prisma/
│   └── ...
├── modules/
│   ├── auth/
│   ├── user/
│   └── ...
├── app.module.ts
└── main.ts
</code></pre>
<p><code>common/</code> folder holds <strong>global concerns and reusable building blocks</strong> that are not tied to any specific domain. Think of it as your project’s toolbox.<br /><code>config/</code> all configuration logic lives here, not scattered in <code>.env</code> calls across the app.<br /><code>lib/</code> folder is for <strong>wrapping third-party libraries or SDKs</strong> so they integrate cleanly into Nest’s DI ecosystem.<br /><code>modules/</code> contains all business domains. Each one can live independently or be plugged into other projects.</p>
<p>This separation helps maintain <em>domain isolation</em>: the <code>auth</code> module shouldn’t need to know about <code>user</code>, and vice versa. It also simplifies testing, you can load only the module you’re working on.</p>
<h2 id="heading-key-architectural-components"><strong>Key Architectural Components</strong></h2>
<p>Here’s what makes up the “core” of the StarterKit:</p>
<h3 id="heading-1-config-module"><strong>1. Config Module</strong></h3>
<p>Centralized environment management, with schema validation.<br />Everything reads from a single source of truth, no magic <code>process.env</code> calls scattered around.</p>
<h3 id="heading-2-prisma-module"><strong>2. Prisma Module</strong></h3>
<p>Instead of wrapping Prisma behind a custom repository layer, the StarterKit uses Prisma <strong>directly</strong> through a dedicated <code>PrismaModule</code> located under <code>lib/prisma</code>.</p>
<p>This keeps things simple and closer to how Prisma is designed to be used, strongly typed, auto-generated, and efficient.<br />Every domain service can inject <code>PrismaService</code> directly for database access, benefiting from full TypeScript type safety and autocomplete on every query.</p>
<h3 id="heading-3-common-utilities"><strong>3. Common Utilities</strong></h3>
<p>Contains:</p>
<ul>
<li><p>Global interceptors and filters (logging, error handling)</p>
</li>
<li><p>Custom pipes and decorators</p>
</li>
<li><p>Shared exceptions and response wrappers</p>
</li>
</ul>
<p>They keep the app consistent and predictable across modules.</p>
<h3 id="heading-4-modules-the-heart-of-the-starterkit"><strong>4.</strong> Modules — the heart of the StarterKit</h3>
<p>Every business domain in the StarterKit lives inside its own <strong>NestJS module</strong> under the <code>modules/</code> directory.<br />This is where the actual <em>business logic</em> resides, the features that make your app what it is.</p>
<p>A typical domain module looks like this:</p>
<pre><code class="lang-markdown">src/modules/user/
├── user.controller.ts
├── user.service.ts
├── user.module.ts
└── dto/
<span class="hljs-code">    ├── create-user.dto.ts
    └── update-user.dto.ts</span>
</code></pre>
<p>Each module is <strong>self-contained</strong> and follows the same conventions:</p>
<ul>
<li><p><strong>Controller</strong> handles HTTP requests, validation, and routing.</p>
</li>
<li><p><strong>Service</strong> contains the domain logic and interacts with Prisma or external services.</p>
</li>
<li><p><strong>DTOs</strong> define the input/output contracts.</p>
</li>
<li><p><strong>Module file</strong> (<code>*.module.ts</code>) wires everything together and exports what other modules may need.</p>
</li>
</ul>
<p>The philosophy is simple:</p>
<blockquote>
<p>“Each domain owns its data, logic, and contracts — no shared global state.”</p>
</blockquote>
<p>This modular layout makes it easy to:</p>
<ul>
<li><p><strong>Add or remove features</strong> without breaking unrelated parts of the system.</p>
</li>
<li><p><strong>Scale horizontally</strong> — you can later extract a module into its own microservice if needed.</p>
</li>
<li><p><strong>Keep team boundaries clear</strong> — each developer or squad can own a module independently.</p>
</li>
</ul>
<h3 id="heading-5-appmodule-as-composition-root"><strong>5. AppModule as Composition Root</strong></h3>
<p><code>AppModule</code> ties everything together.<br />It imports core modules, registers global providers, and bootstraps the dependency graph.</p>
<h2 id="heading-design-alternatives-i-considered"><strong>Design Alternatives I Considered</strong></h2>
<p>I actually explored three different paths before landing here:</p>
<ol>
<li><p><strong>Monolithic structure</strong><br /> ✅ Simple to start, but scales poorly.<br /> ❌ Changes in one area can ripple everywhere.</p>
</li>
<li><p><strong>Microservices</strong><br /> ✅ Great isolation, clear boundaries.<br /> ❌ Overkill for early MVPs; too much infra overhead.</p>
</li>
<li><p><strong>Modular monolith (chosen)</strong><br /> ✅ Keeps isolation <em>and</em> simplicity.<br /> ✅ Easy to scale horizontally later.<br /> ❌ Requires clear boundaries discipline, but that’s a good thing.</p>
</li>
</ol>
<p>In terms of architecture pattern, I leaned toward a <strong>Clean Architecture</strong> style, but not dogmatically, just enough layering to keep things tidy without abstraction fatigue.</p>
<h2 id="heading-developer-experience"><strong>Developer Experience</strong></h2>
<p>Architecture isn’t just about structure; it’s about how it <em>feels</em> to build with it.</p>
<ul>
<li><p>Built-in CLI scripts to scaffold modules quickly.</p>
</li>
<li><p>Opinionated ESLint, Prettier, and commit hooks.</p>
</li>
<li><p>Strict TypeScript setup for safer refactors.</p>
</li>
<li><p>Auto-registration for common NestJS decorators.</p>
</li>
</ul>
<p>The goal: new developers can clone, <code>pnpm start:dev</code>, and start shipping features, no setup pain.</p>
<h2 id="heading-whats-next"><strong>What’s Next</strong></h2>
<p>The next step after architecture is to tackle <strong>Authentication</strong>.<br />In the next article <em>“Building the Auth Module”</em> . I’ll explain how I implemented a flexible auth flow with JWT, refresh tokens, and optional social login, all built on top of this core architecture.</p>
<h2 id="heading-closing-thoughts"><strong>Closing Thoughts</strong></h2>
<p>Good architecture isn’t about complexity, it’s about clarity.<br />When the system’s design reflects how you <em>think</em> about your problem domain, everything else becomes easier: refactoring, testing, even onboarding new devs.</p>
<p>This StarterKit is still evolving, but its foundation feels solid, simple enough for small projects, yet robust enough to grow into something much bigger.</p>
]]></content:encoded></item><item><title><![CDATA[Why I’m Building My Own NestJS StarterKit]]></title><description><![CDATA[Intro
Over the years, I’ve built and maintained quite a few backend projects. Some started from scratch, others from templates, and a few evolved from messy prototypes that somehow made it to production. One thing kept repeating: every new project me...]]></description><link>https://blog.iriyanto.com/why-im-building-my-own-nestjs-starterkit</link><guid isPermaLink="true">https://blog.iriyanto.com/why-im-building-my-own-nestjs-starterkit</guid><category><![CDATA[Engineering Journal]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[Backend Development]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Clean Architecture]]></category><category><![CDATA[Scalable Systems]]></category><category><![CDATA[prisma]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Mon, 06 Oct 2025 01:00:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759641684768/4f0b3a9d-fc81-46bd-a6ba-6269441fa164.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-intro">Intro</h2>
<p>Over the years, I’ve built and maintained quite a few backend projects. Some started from scratch, others from templates, and a few evolved from messy prototypes that somehow made it to production. One thing kept repeating: every new project meant doing the same setup again and again.</p>
<ol>
<li><p>Setting up configs.</p>
</li>
<li><p>Setting up authentication.</p>
</li>
<li><p>Integrating Prisma.</p>
</li>
<li><p>Structuring modules.</p>
</li>
<li><p>Defining error handling, logging, and response formats.</p>
</li>
</ol>
<p>At my last job, I actually built something similar, an internal <strong>SkeletonKit</strong> for the company’s software house projects. It was designed to standardize backend development across multiple client applications and help the team move faster when starting new services. But that project was strictly for internal use, customized for the company’s stack and business needs.</p>
<p>This time, I wanted to build something that goes beyond work. Something open, flexible, and accessible to anyone who loves building scalable backends.</p>
<p>That’s when I decided to create the <strong>NestJS MVP StarterKit</strong>, my own open-source foundation for real-world applications.</p>
<h2 id="heading-the-problem-with-existing-starter-kits">The Problem with Existing Starter Kits</h2>
<p>There are many NestJS starter projects out there, and some of them are great. But most of them live at one of two extremes.</p>
<ol>
<li><p>Too minimal, basically a fresh NestJS install with a few renamed folders.</p>
</li>
<li><p>Too opinionated, forcing you to follow someone else’s architecture or tools.</p>
</li>
</ol>
<p>They often look nice on GitHub, but once you try to use them for a real project, things start falling apart.</p>
<p>Common issues I’ve seen:</p>
<ul>
<li><p>Folder structures that don’t scale well.</p>
</li>
<li><p>Missing modular design and clear domain boundaries.</p>
</li>
<li><p>Reliance on third-party auth like Auth0 or Clerk that creates vendor lock.</p>
</li>
<li><p>Outdated dependencies and inconsistent patterns.</p>
</li>
<li><p>A focus on “getting started fast” instead of “staying maintainable”.</p>
</li>
</ul>
<p>So yes, they’re great for demos, but not for long-term projects.</p>
<h2 id="heading-why-im-doing-this-myself">Why I’m Doing This Myself</h2>
<p>After years of working on backend systems, I realized I didn’t want a “template” anymore. I wanted a <strong>foundation</strong>.</p>
<p>When you’ve built enough APIs, you start to see what keeps breaking and what’s always needed. You get tired of rebuilding the same structure for every new idea.</p>
<p>I needed something clean, extensible, and trustworthy. A base I could spin up quickly for any MVP, but that could also scale without a full rewrite later.</p>
<p>That’s why I decided to build my own starter kit.<br />Not because I think I can do it better than others, but because I want one that fits the way I think about architecture.</p>
<h2 id="heading-the-principles-behind-my-starterkit">The Principles Behind My StarterKit</h2>
<p>This project is more than just a collection of files. It’s a reflection of certain ideas I really believe in.</p>
<ul>
<li><p><strong>DIY Auth and Core Logic</strong><br />  No vendor lock. Full control over the stack and the data.</p>
</li>
<li><p><strong>Scalable Modular Structure</strong><br />  Each domain, like Auth or User, is its own module that can grow independently.</p>
</li>
<li><p><strong>Modern and Predictable</strong><br />  Follows clean architecture patterns without going overboard.</p>
</li>
<li><p><strong>Production-Ready from Day One</strong><br />  Includes configs, Prisma, logging, Docker setup, and Swagger.</p>
</li>
<li><p><strong>Readable and Educational</strong><br />  Every decision should make sense when you read the code. Not just “it works”, but “here’s why it’s written this way”.</p>
</li>
</ul>
<p>Basically, this isn’t just a starter kit. It’s a learning base for real-world backend development with NestJS.</p>
<h2 id="heading-tech-stack-and-design-choices">Tech Stack and Design Choices</h2>
<p>Every tool was chosen with long-term maintenance in mind.</p>
<ul>
<li><p><strong>NestJS</strong> for its structured, modular architecture.</p>
</li>
<li><p><strong>Prisma ORM</strong> for type-safe and elegant database management.</p>
</li>
<li><p><strong>Passport and JWT</strong> for solid authentication and authorization flows.</p>
</li>
<li><p><strong>Docker and Environment Configs</strong> for consistent local and production environments.</p>
</li>
<li><p><strong>Jest</strong> for testing key modules.</p>
</li>
<li><p><strong>Swagger (OpenAPI)</strong> for easy API documentation and collaboration.</p>
</li>
</ul>
<p>It’s intentionally simple on the surface, but powerful once you start building on it.</p>
<h2 id="heading-what-im-trying-to-achieve">What I’m Trying to Achieve</h2>
<p>I’m not doing this to reinvent the wheel. I just want to simplify my own workflow and reduce setup fatigue.</p>
<p>My goals are simple:</p>
<ul>
<li><p>Create a codebase that matches how I think and work.</p>
</li>
<li><p>Make it fast to start new projects without rebuilding boilerplate.</p>
</li>
<li><p>Share it so others who value clean architecture can use it too.</p>
</li>
</ul>
<p>In a way, this project is also my <strong>documentation of thought</strong>, a living reference I can return to, refine, and grow with.</p>
<h2 id="heading-the-journey-ahead">The Journey Ahead</h2>
<p>This isn’t a one-time release. I’ll document it piece by piece, like an engineering journal.</p>
<p>Each core module (Auth, User, Config, Logging, etc.) will have its own breakdown.<br />I’ll explain:</p>
<ul>
<li><p>Why the design choices were made.</p>
</li>
<li><p>What trade-offs were considered.</p>
</li>
<li><p>What alternative patterns might also work.</p>
</li>
</ul>
<p>Because the goal isn’t just to share code. It’s to share <em>thinking</em>.</p>
<h2 id="heading-closing-thought">Closing Thought</h2>
<blockquote>
<p>The best starter kit isn’t the one you download. It’s the one you understand deeply because you built it yourself.</p>
</blockquote>
<p>That’s why I’m building my own NestJS StarterKit.<br />Not because others don’t exist,<br />but because this one will fit <em>me</em> and hopefully help others too.</p>
]]></content:encoded></item><item><title><![CDATA[From Dream Job to Nightmare: The Recruitment Scam That Went Too Far]]></title><description><![CDATA[Intro
Late September 2025 became one of those moments when I had an unexpectedly interesting experience. It all started with a small LinkedIn notification one afternoon. Someone with the initials AP reached out to me and asked if I really knew how to...]]></description><link>https://blog.iriyanto.com/from-dream-job-to-nightmare-the-recruitment-scam-that-went-too-far</link><guid isPermaLink="true">https://blog.iriyanto.com/from-dream-job-to-nightmare-the-recruitment-scam-that-went-too-far</guid><category><![CDATA[recruitment scam]]></category><category><![CDATA[JobScam]]></category><category><![CDATA[remote work]]></category><category><![CDATA[developer tips]]></category><category><![CDATA[online safety]]></category><category><![CDATA[#cybersecurity]]></category><category><![CDATA[phishing]]></category><category><![CDATA[career advice]]></category><category><![CDATA[Scam Awareness]]></category><category><![CDATA[Red Flags]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Sun, 28 Sep 2025 18:12:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FjyseC7iV3k/upload/97fd2aba7cf55a9e17465cf2b74e8906.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-intro"><strong>Intro</strong></h2>
<p>Late September 2025 became one of those moments when I had an unexpectedly interesting experience. It all started with a small LinkedIn notification one afternoon. Someone with the initials AP reached out to me and asked if I really knew how to self-host a Signal server, especially since Signal Messenger’s own documentation on this is quite limited.</p>
<p>I confidently said YES, because I had already written about this topic on my blog <a target="_blank" href="https://blog.iriyanto.com/self-deploy-the-latest-version-of-signal-server-v2025082200">(here)</a>.</p>
<p>A spark of excitement hit me. This felt like a chance to work with Signal Messenger on a larger scale and see it implemented more broadly.</p>
<h2 id="heading-in-short-the-technical-side-of-signal-server">In Short: The Technical Side of Signal Server</h2>
<p>As I explained in my blog article, deploying a Signal Server is very challenging. The documentation is limited, the architecture is highly complex, and it relies on many supporting services.</p>
<p>Signal also uses several premium services from cloud providers. For example, BigTable, which I can’t realistically purchase just for a small-scale setup. Their design is clearly optimized for large-scale deployments, which is why many of their components depend on highly scalable services.</p>
<h2 id="heading-recruitment-contact">Recruitment Contact</h2>
<p>I confidently answered every question from the client in our LinkedIn chat. They became interested in implementing the solution and decided to recruit me. Not just on a contract basis, since they noticed I was open to work, they offered me a full-time remote position for an initial duration of one year, with the possibility of extension.</p>
<p>That was quite appealing. A fully remote job, offered right away as a full-time role. They also suggested continuing the conversation over a WhatsApp voice call. I hesitated for a moment and did a quick background check on their profile, company, and track record. Everything looked “real” and legitimate. The profile was verified, and the person was listed as a Co-Founder and CTO of an Indian company.</p>
<p>Over the next two days, I had several WhatsApp conversations with another person, let’s call them MK, who seemed to be part of HR or perhaps an IT team lead. I also had a voice call with MK to discuss my experience and how I handle Signal Server deployments. Up to this point, nothing felt suspicious. Their technical questions were relevant and reasonable.</p>
<p>After that, MK scheduled an interview for me with their Senior Software Engineer based in the United States. They mentioned that the company in India was a branch of their US headquarters.</p>
<h2 id="heading-red-flags-start-to-appear">Red Flags Start to Appear</h2>
<p>Nothing seemed strange… until I received a WhatsApp message that said,</p>
<blockquote>
<p>Look in your inbox and you will find an email. Reply to it.</p>
</blockquote>
<p>I checked my inbox. Nothing. I checked All Mail. Still nothing. Then I opened the Spam folder and there it was.</p>
<p>The email looked like this:</p>
<blockquote>
<p>Hi Iriyanto</p>
<p>How are you?</p>
<p>Our reference is from our India company, Which you discussed on the LinkedIn.</p>
<p>so you have to send us your latest CV,</p>
<p>The following points are essential in which your actual full name,, as well as gender, date of birth, full address, contact number, email ID, passort number, as well as the name of the university and the name of the college where you passed graduation and what percentage you passed, and the name of the company in which you have worked in the last three years and the main role in which project you have done the details. Which countries in the world have you visited, which country's visa is in your passport? And whether you have applied to any company in USA or India before, If yes, when did you do it and then why did you not accept the application?</p>
</blockquote>
<p>Suspicious? Definitely. They hadn’t even sent me an official offer letter, yet they were already asking for highly sensitive personal data, like passport number, travel history, and visa information.</p>
<p>This felt wrong, so I checked the email domain: <code>tibco-hr-uk@tibco.online</code>. Wait… <code>.online</code> domain? That was odd. I checked the official site <a target="_blank" href="https://www.tibco.com/">tibco.com</a>, it exists, has a verified LinkedIn page, and is a legitimate company with more than 25 years of history. Would a company of that scale really use a <code>.online</code> domain, and from the UK no less, not the US?</p>
<p>I cross-checked the LinkedIn profile of the so-called Senior Software Engineer from the “US branch.” Something didn’t add up. The real TIBCO page was verified, but this one claimed to be TIBCO Software (SA) Pty Limited and was unverified, yet somehow had many followers. Could LinkedIn followers be bought? Possibly.</p>
<p>At this point, my gut told me this was a scam, a clever one, but a scam nonetheless.</p>
<h2 id="heading-escalation-and-manipulation">Escalation and Manipulation</h2>
<p>Of course, I refused to provide such sensitive personal data, especially since I hadn’t even received an official offer letter yet. I politely replied, explaining that I would only share sensitive information once a formal offer was sent and onboarding had begun.</p>
<p>They kept trying to convince me, not through an official <a target="_blank" href="http://tibco.com">tibco.com</a> email address, but by insisting that their Indian company was legitimate. They even sent me what looked like official company documents, including their CIN registration. But at this point, I was already too suspicious. I repeated my stance: I would not share sensitive information unless it came through official <a target="_blank" href="http://tibco.com">tibco.com</a> channels and after receiving an offer letter.</p>
<p>And what did I get in return? Their tone on WhatsApp and email became completely unprofessional, behavior you would never expect from a large, established company.</p>
<p>They began demanding that I verify my LinkedIn account, claiming they rarely make offers to unverified users. Then came the accusations:</p>
<blockquote>
<p>When is a LinkedIn CV valid? When you are verified on LinkedIn, then a LinkedIn CV is considered valid.</p>
<p>We suspect that you may be involved in some criminal activity in your country, which is why you are not disclosing your name.</p>
</blockquote>
<p>Yap… They actually accused me of being a criminal. My real name, nationality, and phone number (+62) were all clearly listed on LinkedIn, and they already had my email and WhatsApp contact.</p>
<p>The messages escalated further:</p>
<blockquote>
<p>you are trying to cheat LinkedIn companies by creating a fake profile on LinkedIn, creating a fake CV, and doing criminal activities on LinkedIn, we have to write it in the LinkedIn report, wait for a while and you will get your result.</p>
<p>We are now feeling that you are not getting a job even in your own country, so you are doing this online because your nature is criminal and arrogant.</p>
</blockquote>
<p>And then came the most extreme message:</p>
<blockquote>
<p>Hello cheater Iriyanto</p>
<p>Tell me your real name and real country? You seem to be the biggest cheater and fraud of the country, or you are a terrorist of your country because you are hiding your identity and lying to everyone.</p>
<p>dont worry We are taking action with you in your own country.</p>
</blockquote>
<p>Yes. They actually called me a terrorist.</p>
<p>The escalation was absurd. In just a few days, I went from being a potential hire to being labeled a criminal and a terrorist, all because I refused to hand over my sensitive personal data.</p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<ul>
<li><p>Never share sensitive personal data before receiving an official offer letter.</p>
</li>
<li><p>Always verify the company’s email domain.</p>
</li>
<li><p>Legitimate companies will never threaten or intimidate candidates.</p>
</li>
<li><p>Watch for red flags early. Trust your instincts and investigate before moving forward.</p>
</li>
</ul>
<h2 id="heading-practical-tips-for-other-developers">Practical Tips for Other Developers</h2>
<ul>
<li><p>Verify the company: check their email domain, cross-check on MCA (if it’s an Indian company), look them up on stock exchanges, and confirm through their official website.</p>
</li>
<li><p>Use safe platforms for communication, such as LinkedIn, Upwork, or reputable job boards.</p>
</li>
<li><p>Don’t be afraid to say “no” to unreasonable requests.</p>
</li>
<li><p>Keep all evidence and report suspicious activity as phishing or scam.</p>
</li>
</ul>
<h2 id="heading-closing">Closing</h2>
<p>Looking back, I felt disappointed. I was excited at first, but relieved that I got out without becoming a victim.</p>
<p>My message: stay cautious. Even the most convincing job offer can turn out to be a scam.</p>
<p>I encourage you to stay alert and share experiences like this so other developers don’t fall into the same trap.</p>
]]></content:encoded></item><item><title><![CDATA[Why I use NestJs for past 5 years]]></title><description><![CDATA[Introduction
It’s hard to believe, but I’ve been using NestJS for almost five years now. My journey started back in college when I was offered a project to build a backend system for a vessel tracking website based on MarineTraffic. At that time, I h...]]></description><link>https://blog.iriyanto.com/why-i-use-nestjs-for-past-5-years</link><guid isPermaLink="true">https://blog.iriyanto.com/why-i-use-nestjs-for-past-5-years</guid><category><![CDATA[nestjs]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Express.js]]></category><category><![CDATA[fastify]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Thu, 18 Sep 2025 17:40:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1758216899850/6cf608dc-8e8d-47cb-9e1f-468f51097674.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>It’s hard to believe, but I’ve been using NestJS for almost five years now. My journey started back in college when I was offered a project to build a backend system for a vessel tracking website based on MarineTraffic. At that time, I had just begun learning Node.js and this was my very first real-world project using it.</p>
<p>When facing a real project, the first questions that came to mind were: What are the requirements? What kind of backend architecture would be suitable? And most importantly, which framework could support those needs? My search for the right framework naturally began with the most popular option at the time, one and only Express.js.</p>
<p>Express, with its tagline <em>“Fast, unopinionated, minimalist web framework for Node.js,”</em> seemed appealing at first. The flexibility was impressive, almost too impressive. While exploring, I quickly found myself falling into a rabbit hole: Which companion packages should I use? How should the folder structure look? Should I follow MVC? Should I write in JavaScript or switch to TypeScript? And the list of questions kept growing.</p>
<p>That’s when I realized I might need to refine my search. Instead of looking for a minimal framework, I needed something more structured, an <em>opinionated</em> Node.js framework. And that’s when I discovered NestJS.</p>
<h1 id="heading-key-reasons-ive-stuck-with-nestjs-for-5-years">Key Reasons I’ve Stuck with NestJS for 5 Years</h1>
<ol>
<li><p><strong>TypeScript as a First-Class Citizen</strong></p>
<p> TypeScript by default? Yes, please! Many developers argue that strongly typed languages make things unnecessarily complicated. That might be true for small projects, but when you’re building something more solid, TypeScript acts like a safety net for JavaScript. It saves you from those dreaded runtime type errors that can appear out of nowhere.</p>
<p> With TypeScript, functions, objects, and even complex structures come with helpful snippets and autocomplete support. This speeds up development and reduces guesswork. NestJS embraces TypeScript from the ground up. When creating a new NestJS project, TypeScript is the default choice, though you can still opt for JavaScript if you want.</p>
<p> But honestly, once you’ve enjoyed the safety, clarity, and productivity boost of TypeScript, who would want to go back to plain JavaScript?</p>
</li>
<li><p><strong>Clean Architecture</strong></p>
<p> NestJS introduces a modular folder structure that feels both organized and intuitive. This is quite different from the traditional MVC approach, where you typically separate files by their function, controllers go into a controller folder, models into a model folder, and so on.</p>
<p> With NestJS, the modular concept groups everything based on the domain of your application. For example, the UserModule contains the controller, service, entity, and repository related to users. If there’s an error in login, you know exactly where to look, the AuthModule. Everything related to authentication stays inside that module and nowhere else.</p>
<p> This modular design makes projects much easier to navigate and maintain, especially as they grow larger. Instead of hunting across multiple folders, you can focus on a single module that fully encapsulates a feature or domain.</p>
</li>
<li><p><strong>Dependency Injection</strong></p>
<p> Impressed with NestJS’s modular architecture? Hold on… it gets even better once you meet its powerful dependency injection (DI) system. Since NestJS takes inspiration from Angular, it also adopts Angular’s DI pattern.</p>
<p> Modules in NestJS become even more powerful thanks to DI. For example, if Module A needs to use a service from Module B, you simply export the service in Module B and import that module into Module A. The DI container will take care of providing the instance wherever it’s needed.</p>
<p> Another benefit of DI is performance optimization. By default, NestJS services are singletons, meaning only one instance is created and shared across the application. This saves memory and ensures consistent state management. Of course, if your use case requires it, you can also configure services to be request-scoped or transient.</p>
<p> In short, DI in NestJS isn’t just a convenience, it’s a cornerstone that keeps your application clean, modular, and efficient.</p>
</li>
<li><p><strong>Recommended Packages</strong></p>
<p> Remember the headache of choosing companion packages in Express? With NestJS, that problem is greatly simplified. The framework not only recommends optimal packages but also provides official wrapper modules for seamless integration.</p>
<p> Take Mongoose, for example. Instead of wiring it up manually, NestJS offers @nestjs/mongoose, a dedicated module that integrates perfectly with the framework’s architecture. Need validation? NestJS supports class-validator and class-transformer out of the box. Need security? There’s built-in support for tools like helmet. Want API documentation? The @nestjs/swagger module automatically generates a complete OpenAPI specification from your controllers and DTOs, no need to handwrite specs.</p>
<p> By curating and wrapping popular libraries, NestJS saves developers from decision fatigue and ensures best practices are followed by default.</p>
</li>
<li><p><strong>Community &amp; Documentation</strong></p>
<p> Back in 2020, there was no ChatGPT to turn to when you got stuck. The first lifeline was always documentation. Thankfully, NestJS provides one of the most beginner-friendly documentations out there. It’s structured almost like a step-by-step guide, yet it doesn’t shy away from diving deep into technical details when needed.</p>
<p> The second lifeline was, of course, the community. The NestJS Discord server has been active for years, and asking questions there often brings direct answers from experienced developers, including some of the core contributors themselves. The combination of high-quality documentation and a responsive community makes NestJS approachable for beginners while still powerful for advanced users.</p>
</li>
</ol>
<h1 id="heading-comparison-with-other-frameworks">Comparison with Other Frameworks</h1>
<ul>
<li><p><strong>Express: great for getting started quickly, but less structured as your application grows</strong></p>
<p>  Express offers a high level of flexibility. For developers who already have strong patterns and architectural practices in place, Express can feel like the perfect choice. It’s lightweight and works especially well for small services or microservices that handle a single responsibility, such as authentication or login endpoints. However, without clear conventions, large-scale projects in Express can become difficult to maintain, especially for teams with varying levels of experience.</p>
</li>
<li><p><strong>Koa / Hapi / Fastify: flexible but less opinionated</strong></p>
<p>  Similar to Express, frameworks like Koa, Hapi, and Fastify focus on flexibility and performance. They give developers freedom to choose how to structure their applications. While this can be powerful, it didn’t match what I needed at the time.</p>
<p>  An opinionated framework like NestJS provides more than just convenience, it creates consistency. For a new team member, onboarding becomes easier: just follow the documentation and established patterns. For experienced developers, the workflow remains consistent because everyone is aligned by the same architectural design. This shared structure reduces confusion, accelerates collaboration, and keeps projects maintainable as they scale.</p>
</li>
</ul>
<h1 id="heading-real-world-use-cases">Real-World Use Cases</h1>
<p>Over the years, I’ve implemented NestJS in a wide range of backend systems: asset management, ticketing systems, auction platforms, e-commerce applications, university education systems, and more. Even projects requiring real-time capabilities, like the vessel tracker I worked on, were achievable thanks to WebSocket and Socket.IO support.</p>
<p>Scaling with NestJS is straightforward thanks to its modular architecture. In one project, the authentication feature became the most frequently accessed part of the system, since every login and token validation request passed through it.</p>
<p>Scaling up the entire application just to handle this load would have been wasteful. Instead, we were able to extract the AuthModule and run it as a separate service within the same NestJS ecosystem. That way, only the authentication workload was scaled independently, without touching the rest of the application.</p>
<p>This modular design makes NestJS highly adaptable, whether you’re building monoliths, microservices, or a hybrid architecture.</p>
<h1 id="heading-lessons-learned-over-the-years">Lessons Learned Over the Years</h1>
<ul>
<li><p><strong>Consistent Code Conventions</strong></p>
<p>  NestJS’s opinionated design enforces best practices by default. This means every project follows a familiar structure, regardless of when it was created or who worked on it. Switching between projects or even revisiting an old one, feels seamless because the folder organization and code patterns remain consistent. This consistency reduces cognitive load, speeds up onboarding, and helps teams maintain codebases more effectively over time.</p>
</li>
<li><p><strong>Easier Testing with Dependency Injection</strong></p>
<p>  One of the most frustrating parts of writing tests is setting up all the services and dependencies you need. Thanks to NestJS’s built-in dependency injection system, this process becomes much simpler.</p>
<p>  With the @nestjs/testing package, you can create a TestingModule that automatically provides the required services and their dependencies. This makes it easy to instantiate only what you need for the test, without manually wiring everything together.</p>
<p>  Dependency Injection not only simplifies test setup but also encourages cleaner, more maintainable unit tests across your application.</p>
</li>
<li><p><strong>Faster Onboarding for New Developers</strong></p>
<p>  When junior developers ask me which Node.js framework they should learn first, my answer is always NestJS. Several juniors I’ve mentored started with it, and they adapted quickly. The opinionated structure and clear documentation allowed them to focus less on setup and more on delivering features. In no time, they were completing the tasks I assigned with confidence.</p>
</li>
</ul>
<h1 id="heading-drawbacks-of-nestjs">Drawbacks of NestJS</h1>
<ul>
<li><p><strong>Steep Learning Curve at First</strong></p>
<p>  It’s true! The first time you try NestJS, the learning curve can feel steep. Concepts like decorators, dependency injection, and other unfamiliar terms may seem overwhelming at the beginning.</p>
<p>  But don’t worry. NestJS is often described as “easy to learn, hard to master.” You can start building applications quickly without needing to fully understand every concept under the hood. For example, it’s enough to know what a decorator does without diving into its internals.</p>
<p>  As you grow more comfortable, NestJS reveals its depth. Exploring advanced concepts allows you to fine-tune performance, optimize architecture, and unlock the full potential of the framework.</p>
</li>
<li><p><strong>Quite a Bit of Boilerplate</strong></p>
<p>  Unlike Express, where you can get a basic API running in just a few lines, NestJS requires more code upfront when starting a project. At first, this can feel like a lot of boilerplate.</p>
<p>  The good news is that NestJS provides a powerful CLI to help. Need to create a module? Just run nest g module <code>&lt;module-name&gt;</code>. Want a complete resource with controller and service? The CLI can generate that too.</p>
<p>  These tools make it easy to manage the initial code overhead, and the comprehensive CLI effectively offsets the boilerplate, allowing you to focus on building features rather than wiring up structure.</p>
</li>
<li><p><strong>Sometimes Feels Too “Opinionated”</strong></p>
<p>  This really comes down to personal preference. Some developers see it as a drawback, while others see it as a strength. It’s a double-edged sword. There are moments when creating even a single module feels like a lot of coding, even with the CLI helping out. But once it’s set up, the structure is solid, maintainable, and consistent. That initial extra effort pays off in the long run, especially as projects grow larger or when new team members join.</p>
</li>
</ul>
<h1 id="heading-conclusion">Conclusion</h1>
<p>If you ask me which Node.js backend framework I recommend, my answer is always NestJS. Even today, I continue to use it whenever the project ecosystem is Node.js.</p>
<p>If you face the same challenges I encountered back in 2020, chances are you’ll end up discovering NestJS at the end of your search. It’s ideal for building solid backends, quickly delivering MVPs, and scaling both vertically and horizontally.</p>
<p>After five years of experience with NestJS, I’m now preparing a starter template that makes it even easier to get a backend up and running for MVP projects. I’ll share the GitHub repository soon, so stay tuned.</p>
]]></content:encoded></item><item><title><![CDATA[Self Deploy the Latest Version of Signal Server v20250822.0.0]]></title><description><![CDATA[Introduction
Signal has become one of the most trusted messaging platforms in the world, well known for its strong emphasis on privacy and security. Unlike many other messaging apps, Signal implements end-to-end encryption by default, ensuring that n...]]></description><link>https://blog.iriyanto.com/self-deploy-the-latest-version-of-signal-server-v2025082200</link><guid isPermaLink="true">https://blog.iriyanto.com/self-deploy-the-latest-version-of-signal-server-v2025082200</guid><category><![CDATA[signal-server]]></category><category><![CDATA[deploy-signal]]></category><category><![CDATA[signal]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Thu, 21 Aug 2025 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/pr5lUMgocTs/upload/0240e029e0f61656c6a834468ab0cf98.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Signal has become one of the most trusted messaging platforms in the world, well known for its strong emphasis on privacy and security. Unlike many other messaging apps, Signal implements end-to-end encryption by default, ensuring that no one—not even the Signal team—can access the content of your conversations. This commitment to security and user freedom is one of the main reasons why individuals, organizations, and even governments rely on Signal for secure communication.</p>
<p>Beyond being a secure messenger, Signal is also <strong>open source</strong>, which means its codebase is publicly available for anyone to review, audit, and even contribute to. This transparency not only builds trust but also allows developers to experiment with self-hosting their own Signal infrastructure.</p>
<p>However, while the idea of running your own Signal server sounds exciting, the reality is far from simple. As highlighted in <a target="_blank" href="https://softwaremill.com/can-you-self-host-the-signal-server/?utm_source=chatgpt.com">SoftwareMill’s article</a>, <a target="_blank" href="https://softwaremill.com/can-you-self-host-the-signal-server/?utm_source=chatgpt.com">deploying the Signal</a> server is <strong>highly complex</strong>, requiring significant technical knowledge, specific dependencies, and ongoing maintenance to keep it in sync with Signal’s rapid development cycle.</p>
<p>To make matters more complicated, if you search for tutorials online with the keyword <em>“how to deploy signal server”</em>, most of what you’ll find is <strong>outdated</strong>. Signal’s server code has evolved drastically over the years, rendering older guides nearly useless. Many of those instructions no longer apply to the current architecture or dependencies of Signal Server.</p>
<p>Despite all these challenges, I managed to successfully deploy the <strong>latest version of Signal Server (v20250822.0.0)</strong>. If you are interested in deploying Signal on your own infrastructure—whether for research, personal use, or within your company—you don’t have to start from scratch. You can reach out to me for guidance and support in setting up your own secure Signal environment.</p>
<h2 id="heading-before-you-start">Before You Start</h2>
<p>In recent years, Signal has shifted much of its infrastructure to rely heavily on <strong>cloud-based services</strong>. This transition makes sense, as it allows Signal to scale globally and handle millions of concurrent users with high availability. Many of the critical components that power Signal—such as messaging queues, storage layers, and push notification services—are tightly integrated with cloud platforms.</p>
<p>For anyone attempting to run Signal Server on their own, this introduces some important considerations. Even if your goal is to deploy Signal locally, you will still need access to several of these cloud services. The good news is that many providers offer a <strong>free tier</strong>, which can be leveraged to set up a working environment without incurring high costs. With some careful configuration, it is possible to replicate much of the production setup while staying within free usage limits.</p>
<p>If your objective is to run Signal in a <strong>fully local environment</strong>, things become more complicated. This typically requires either modifying third-party code or setting up self-hosted replacements for cloud services using tools like <strong>LocalStack</strong>. While this is certainly achievable, it adds significant complexity and can make maintenance much more difficult in the long run.</p>
<p>In this article, we will take a more practical approach: deploying the latest version of Signal Server <strong>without heavily modifying its dependencies or services</strong>. The focus will be on getting Signal up and running with minimal adjustments, making it possible for you to experiment with a self-hosted deployment while still understanding how Signal’s infrastructure pieces fit together.</p>
<h2 id="heading-preparation-amp-requirements">Preparation &amp; Requirements</h2>
<p>Before diving into the deployment steps, it’s important to understand the foundational requirements for running Signal Server. A successful deployment doesn’t just depend on the main server itself. It involves hardware, multiple cloud services, domain configurations, and a variety of supporting components. Below are the key elements you’ll need to prepare:</p>
<ul>
<li><h3 id="heading-hardware">Hardware</h3>
<p>  For this deployment, I used a <strong>VPS running Ubuntu 24</strong> with specifications of <strong>4 CPU cores and 8 GB RAM</strong>. This provides enough resources to handle the compilation, dependencies, and basic operation of Signal Server.</p>
</li>
<li><h3 id="heading-cloud-services">Cloud Services</h3>
<p>  Signal relies heavily on cloud infrastructure. In our setup, we will be using:</p>
<ul>
<li><p>AWS (DynamoDB, S3, etc.)</p>
</li>
<li><p>GCP (Firebase, Google Cloud Storage, etc.)</p>
</li>
<li><p>Cloudflare for DNS and security layers</p>
</li>
</ul>
</li>
</ul>
<p>    …and several other services that integrate into the overall system.</p>
<ul>
<li><h3 id="heading-domain">Domain</h3>
<p>  Signal itself makes use of multiple subdomains for different services. While <a target="_blank" href="http://chat.signal.org"><code>chat.signal.org</code></a> acts as the <strong>main messaging endpoint</strong>, other subdomains such as <a target="_blank" href="http://storage.signal.org"><code>storage.signal.org</code></a>, <a target="_blank" href="http://svr.signal.org"><code>svr.signal.org</code></a>, and various TURN servers support additional functionality. A proper domain setup is crucial to mirror this architecture in your self-hosted deployment.</p>
</li>
<li><h3 id="heading-other-services">Other Services</h3>
<p>  It’s important to note that the <strong>Signal Server is only the core service</strong>. Many smaller supporting services such as storage, push notifications, and real-time communication helpers must be integrated for the system to function correctly. We will explore these dependencies in more detail later in this guide.</p>
</li>
</ul>
<h2 id="heading-get-the-source-code">Get the Source Code</h2>
<p>Signal Server’s code is available on GitHub:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/signalapp/Signal-Server.git
</code></pre>
<p>After cloning, switch to the latest version first (in this case v20250822.0.0). You can find the available versions in the GitHub repository under the tags section.</p>
<pre><code class="lang-bash">git checkout -b latest v20250822.0.0
git <span class="hljs-built_in">log</span>

commit dbbd9134455affb83b350bb2121e4c8dc39dac0b (HEAD -&gt; latest, tag: v20250822.0.0)
Author: Ravi Khadiwala ravi@signal.org
Date:   Fri Aug 22 11:40:16 2025 -0500

    Allow downgrade on SQPR capability
</code></pre>
<h2 id="heading-build-and-run">Build and Run</h2>
<p>Signal Server is written in <strong>Java</strong>, and the latest release requires <strong>Java 24 (Temurin distribution)</strong> along with <strong>Maven (mvn)</strong> for building the project. Before attempting to compile, make sure both Java and Maven are properly installed and configured in your environment, as mismatched versions or missing dependencies will cause the build process to fail.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> Signal-Server
mvn clean install -DskipTests
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756447236859/57c79bed-883e-4888-a5ee-c8ec98819a7e.png" alt class="image--center mx-auto" /></p>
<p>Once the compilation is complete, you will find the generated <code>TextSecureServer-20250822.0.0.jar</code> file located in the <code>service/target</code> directory. This JAR file serves as the <strong>main executable</strong> for running the Signal Server. From this point onward, it becomes the core binary you’ll use to start and manage your self-hosted Signal instance. Try to run it:</p>
<pre><code class="lang-bash">java -jar service/target/TextSecureServer-20250822.0.0.jar server
</code></pre>
<p>After trying to run the server, I encountered the error:</p>
<p><code>no main manifest attribute, in service/target/TextSecureServer-20250822.0.0.jar</code>.</p>
<p>After a moment of reflection (and questioning my life choices), I discovered the root cause: there is a module called <code>spam-filter</code> that points to a private repository — <a target="_blank" href="https://github.com/signalapp/Signal-Server/blob/main/spam-filter?utm_source=chatgpt.com">https://github.com/signalapp/Signal-Server/blob/main/spam-filter</a>. Since this repository is private, we don’t have access to it.</p>
<p>It seems that this module is intentionally private because it contains critical features related to spam protection, including <code>spamChecker</code>, <code>challengeConstraintChecker</code>, <code>registrationFraudChecker</code>, <code>registrationRecoveryChecker</code>, and <code>captchaClientSupplier</code>.</p>
<p>To work around this limitation, we need to <strong>exclude the module</strong> by adding the build flag <code>-Pexclude-spam-filter</code> when compiling the application then run it again.</p>
<pre><code class="lang-bash">mvn clean install -DskipTests -Pexclude-spam-filter
java -jar service/target/TextSecureServer-20250822.0.0.jar server
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756448113190/0f9debef-f129-4c74-96be-09f863656999.png" alt class="image--center mx-auto" /></p>
<p>It looks like this time it’s not the same error as before, but rather a <strong>missing credentials issue</strong>. Inside the <code>service/config</code> folder, there are two configuration files that we need to provide when running the JAR file:</p>
<ul>
<li><p>The first one is <code>sample-secrets-bundle.yml</code>, which we include using the flag:</p>
<pre><code class="lang-bash">  -Dsecrets.bundle.filename=sample-secrets-bundle.yml
</code></pre>
</li>
<li><p>The second one is the main <code>sample.yml</code> file, which is appended at the end of the command after <code>server</code>.</p>
</li>
</ul>
<p>So the full command to run Signal Server is:</p>
<pre><code class="lang-bash">java -Dsecrets.bundle.filename=sample-secrets-bundle.yml -jar service/target/TextSecureServer-20250822.0.0.jar server sample.yml
</code></pre>
<p>After solving that issue, I thought the server would finally run and be ready to use. But reality can be harsh. In fact, this was only the <strong>beginning of the Signal Server setup journey</strong>. What followed was a cycle of endless trial and error, challenge after challenge—so much so that I started questioning whether I had made the wrong choices in life.</p>
<p>Still, no matter how tough it gets, each obstacle has to be solved one by one—and with a sense of pride for every small victory along the way.</p>
<h2 id="heading-server-configuration">Server Configuration</h2>
<p>Based on my review of the <code>sample.yml</code> and <code>sample-secrets-bundle.yml</code> files, here is a summary of the key configuration sections:</p>
<ul>
<li><p><strong>Logging</strong>: This can be directed to the console. Integration with Datadog can be disabled (since it’s a paid service).</p>
</li>
<li><p><strong>Metrics</strong>: Can be directed either to the console or stored in a CSV file.</p>
</li>
<li><p><strong>TlsKeyStore</strong>: This can be left as default.</p>
</li>
<li><p><strong>Payments (Stripe, Braintree, Google Play Billing, Apple App Store, PaymentsService)</strong>: Subscriptions and one-time donations can be left as default or ignored entirely if you don’t need payment integration.</p>
</li>
<li><p><strong>AppleDeviceCheck &amp; DeviceCheck</strong>: Leave as default (their exact purpose is unclear for now, so we’ll leave them untouched).</p>
</li>
<li><p><strong>DynamoDB &amp; DynamoDB Tables</strong>: This is the <strong>core configuration</strong> for Signal Server’s database. Proper setup is critical.</p>
</li>
<li><p><strong>pagedSingleUseKEMPreKeyStore</strong>: Used to store keys, which must be configured with <strong>AWS S3</strong>.</p>
</li>
<li><p><strong>Redis Clusters (cacheCluster, pubsub, pushSchedulerCluster, rateLimitersCluster, messageCache)</strong>: These require Redis in cluster mode. Running this in the cloud can be expensive, so I configured a <strong>Redis cluster on a single VM</strong> instead (I’ll explain how to set this up later in another article).</p>
</li>
<li><p><strong>directoryV2</strong>: Can remain as default. This is related to the <strong>Contact Delivery Service</strong>.</p>
</li>
<li><p><strong>svr2, svrb, storageService, registrationService, keyTransparencyService</strong>: These are supporting services that work alongside the main Signal Server. We’ll discuss them in detail later.</p>
</li>
<li><p><strong>gcpAttachments &amp; tus</strong>: Used for <strong>attachment uploads</strong>.</p>
</li>
<li><p><strong>apn &amp; fcm</strong>: Required for <strong>push notifications</strong>.</p>
</li>
<li><p><strong>cdn &amp; cdn3StorageManager</strong>: Handle profile pictures, attachments, and related media.</p>
</li>
<li><p><strong>dogstatsd &amp; openTelemetry</strong>: These can be safely ignored.</p>
</li>
<li><p><strong>unidentifiedDelivery, zkConfig, callingZkConfig, backupsZkConfig</strong>: These are critical configurations because they are directly tied to <strong>libsignal</strong>. They must be set up properly.</p>
</li>
<li><p><strong>dynamicConfig</strong>: A dynamic configuration stored in <strong>AWS S3</strong> that continuously syncs with the application. This is also very important.</p>
</li>
</ul>
<h2 id="heading-other-services-1">Other Services</h2>
<p>These are the supporting services that must also be set up for the main <strong>Signal Server</strong> to run properly. Each of these services has its own configuration, but for now, I’ll just summarize what they are and their functions. I may cover the detailed setup instructions in future articles.</p>
<ul>
<li><p><strong>Registration Service</strong>: A core dependency that enables users to register. It handles the creation of registration sessions, which require users to verify their phone numbers.</p>
</li>
<li><p><strong>Storage Service</strong>: Essential for group functionality. Without this service, users cannot create groups or perform group-related activities.</p>
</li>
<li><p><strong>Coturn</strong>: Signal has migrated from self-hosted Coturn to Cloudflare’s TURN service. Fortunately, Cloudflare provides a monthly quota for free usage, and charges only once that quota is exceeded.</p>
</li>
<li><p><strong>Signal Calling Service</strong>: While the TURN server handles one-to-one calls, this service enables <strong>group calls and video calls</strong>. Without it, group calling would not be possible.</p>
</li>
<li><p><strong>Tus</strong>: Handles file uploads. Signal is transitioning from Google Cloud Storage (GCS) to this service. For now, you can override it with GCS and safely ignore it, since Tus needs to be deployed on Cloudflare Workers (a paid service).</p>
</li>
<li><p><strong>Contact Discovery Service</strong>: A resource-intensive service that requires <strong>Intel SGX</strong>. Its function is to map user contacts to determine whether they already have a Signal account.</p>
</li>
<li><p><strong>Secure Value Recovery v2</strong>: Another <strong>SGX-dependent</strong> service, responsible for verifying a user’s PIN during account recovery.</p>
</li>
<li><p><strong>Key Transparency Service</strong>: Protects against <strong>man-in-the-middle attacks</strong> by ensuring that the public key received by a client truly belongs to the intended user. It also provides auditability and prevents the server from manipulating keys.</p>
</li>
</ul>
<p>With these services in place, you should have everything needed to run a functional <strong>Signal Server</strong>. Some services may not be explicitly listed here, as they are used primarily on the <strong>client side</strong> (mobile and desktop), so additional tracing may be required to identify any missing components.</p>
<p>But with that said, this marks the (hopefully final) milestone in setting up Signal Server. Here’s a screenshot of victory in this battle:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756453017678/6b35e41e-1669-43d0-9a30-c6e445ca4313.png" alt class="image--center mx-auto" /></p>
<p>Setting up the latest version of Signal Server is far from a simple task. From dealing with private modules, cloud dependencies, and countless supporting services, the journey requires patience, persistence, and a deep dive into Signal’s evolving architecture. But in the end, the reward is worth it: the ability to run your very own self-hosted instance of one of the most secure messaging platforms in the world.</p>
<p>If you’re interested in deploying Signal Server for <strong>research, personal use, or within your organization</strong>, you don’t have to go through the same trial-and-error process I did. I can help you streamline the setup and get your own Signal Server running smoothly.</p>
<p>👉 Feel free to reach out to me if you’d like assistance with your <strong>Signal Server deployment</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[How to Compile and Run the Latest Version of Signal App (Android) v7.55.0]]></title><description><![CDATA[Introduction
Signal has always fascinated me, not only because of its focus on privacy, but also because it’s fully open source. This means you’re free to dive into the source code, learn how things work under the hood, and even compile your own vers...]]></description><link>https://blog.iriyanto.com/how-to-compile-and-run-the-latest-version-of-signal-app-android-v7550</link><guid isPermaLink="true">https://blog.iriyanto.com/how-to-compile-and-run-the-latest-version-of-signal-app-android-v7550</guid><category><![CDATA[signal-app]]></category><category><![CDATA[signal-android]]></category><category><![CDATA[signal]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Tue, 19 Aug 2025 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HfWA-Axq6Ek/upload/2ea4218bd284e3632ca4bdecb80702b5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Signal has always fascinated me, not only because of its focus on privacy, but also because it’s <strong>fully open source</strong>. This means you’re free to dive into the source code, learn how things work under the hood, and even compile your own version of the app.</p>
<p>In this post, I’ll walk you through how I compiled and ran the <strong>latest version of Signal Android (v7.55.0)</strong> on my local machine. This isn’t meant to be a super detailed tutorial, but more of a starting point if you’re curious about how Signal works behind the scenes.</p>
<h2 id="heading-step-1-get-the-source-code">Step 1: Get the Source Code</h2>
<p>Signal Android’s code is available on GitHub:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/signalapp/Signal-Android.git
</code></pre>
<p>After cloning, switch to the latest version first (in this case v7.55.0). You can find the available versions in the GitHub repository under the tags section.</p>
<pre><code class="lang-bash">git checkout -b latest v7.55.0
git <span class="hljs-built_in">log</span>

commit 8e9dc7895757f7e7bf7b2f3ac5b283371714caa5 (HEAD -&gt; latest, tag: v7.55.0, origin/main, origin/HEAD, main)
Author: Michelle Tang mtang@signal.org
Date:   Wed Aug 27 16:09:44 2025 -0400

    Bump version to 7.55.0
</code></pre>
<h2 id="heading-step-2-open-android-studio">Step 2: Open Android Studio</h2>
<p>When you open the Signal Android project in Android Studio, the IDE runs a <strong>Gradle sync</strong>, which means it reads the project’s configuration files (build.gradle, settings.gradle, etc.) and sets up the project structure accordingly. During this process, Gradle also checks for any required libraries or plugins defined in the configuration. If those dependencies are not already available in your local cache, Android Studio will automatically download them from repositories such as Maven Central or Google’s Maven. That’s why you often see a “Sync” and “Downloading” process the first time you open the project or after clearing caches—it ensures the project has everything it needs to build and run correctly. As you can see in the screenshot below, just wait until the sync process finishes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756373366898/054d369c-3edb-47d4-968d-a656022c1c61.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-3-build-the-app">Step 3: Build the App</h2>
<p>After the sync is complete, the first step is to build the project. This ensures that all the code and dependencies compile correctly and the project structure is set up properly. In Android Studio, you can build the project by going to the top menu and selecting <strong>Build → Make Project</strong>, or by using the shortcut <strong>Ctrl+F9</strong> (Cmd+F9 on Mac). Building the project at this stage helps identify any missing dependencies, compilation errors, or configuration issues before you attempt to run the app.</p>
<h2 id="heading-step-4-run-it-locally">Step 4: Run It Locally</h2>
<p>Once the project builds successfully, you can run the app to see it in action. In Android Studio, click the <strong>Run</strong> button (the green triangle) in the toolbar, or use the shortcut <strong>Shift+F10</strong> (on Windows/Linux) or <strong>Control+R</strong> (on Mac). You will then be prompted to select a target device, which can be either an Android emulator or a physical device connected via USB with developer mode enabled. Running the app allows you to verify that it launches correctly and that the initial setup is working before making any changes to the code.</p>
<p>After running the application, you can try registering a new account because the app has already been configured to connect to the Signal server. This setup allows you to test the app’s core features, such as sending and receiving messages, without needing any additional configuration. It’s a great way to verify that your local build is working correctly and that the connection to the server is properly established.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756375252849/a0f50f31-a559-4e2a-9704-748b7d81042d.png" alt class="image--center mx-auto" /></p>
<p>To check which version of Signal Android is currently installed on your device, open the app and navigate to <strong>Settings → Help</strong>. This section not only shows the app version but can also provide useful information for troubleshooting, ensuring that you are using the correct version, and confirming that your app is up to date before making any changes or updates.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756375290083/595e24e4-ea90-457a-971e-a1cf303c2a4b.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-this-matters">Why This Matters</h2>
<p>Compiling Signal yourself gives you:</p>
<ul>
<li><p>Transparency — you know exactly which version of the code you’re running.</p>
</li>
<li><p>Learning — a chance to understand how a real-world secure messaging app is structured.</p>
</li>
<li><p>Flexibility — the first step if you ever want to connect Signal Android to your <strong>own self-hosted Signal server</strong>.</p>
</li>
</ul>
<h2 id="heading-whats-next">What’s Next</h2>
<p>In future posts, I’ll dive deeper into other parts of the ecosystem:</p>
<ul>
<li><p>Deploying a Signal Server</p>
</li>
<li><p>Connecting your custom Android client</p>
</li>
<li><p>and more…</p>
</li>
</ul>
<p>This is just the beginning of exploring Signal from an open source and self-hosted perspective 🚀</p>
<h2 id="heading-notes">Notes</h2>
<p>This series is meant to document my journey in exploring and deploying Signal. If you’re also curious about running your own Signal server but don’t want to go through all the setup pain, I can help you get started. Just reach out!</p>
]]></content:encoded></item><item><title><![CDATA[Why Signal Caught My Attention as an Open Source Project]]></title><description><![CDATA[When it comes to messaging apps, most of us are already familiar with WhatsApp, Telegram, or even good old SMS. But a few years ago, I stumbled upon Signal, and it quickly became one of the most fascinating projects I’ve ever explored.
What makes Sig...]]></description><link>https://blog.iriyanto.com/why-signal-caught-my-attention-as-an-open-source-project</link><guid isPermaLink="true">https://blog.iriyanto.com/why-signal-caught-my-attention-as-an-open-source-project</guid><category><![CDATA[signal]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[messaging]]></category><category><![CDATA[privacy]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Sun, 17 Aug 2025 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/BuPiOZN5DOQ/upload/ece3d8ffda8147259d0a6f3992617806.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When it comes to messaging apps, most of us are already familiar with WhatsApp, Telegram, or even good old SMS. But a few years ago, I stumbled upon <strong>Signal</strong>, and it quickly became one of the most fascinating projects I’ve ever explored.</p>
<p>What makes Signal special isn’t just the fact that it offers <strong>end-to-end encryption by default</strong> (although that’s already a big deal). For me, the real highlight is that <strong>Signal is fully open source</strong>—both the app and the server.</p>
<p>That’s important because it means anyone can audit the code, understand how it works, and even contribute to its development. There’s no “black box” or hidden trick in how your messages are handled. Compared to other chat apps owned by big corporations, Signal stands out as a privacy-first platform that is transparent by design.</p>
<p>As someone really passionate about open source, this resonates with me a lot. In Indonesia, it’s not very common to see local developers getting deeply involved with starting and maintaining open source projects, but tools like Signal show us what’s possible when a community comes together to build something meaningful.</p>
<p>In fact, discovering Signal is one of the reasons I became more interested in exploring how self-hosting could work for messaging platforms. Imagine running your own secure messaging server—having complete control over your data, while still using the same strong encryption that Signal is known for.</p>
<p>That’s where my curiosity started, and it’s a topic I’ll be writing more about in this series. In the next post, I’ll dive into what it actually takes to <strong>self-host Signal</strong>—the benefits, the challenges, and why it might be worth considering if privacy and data control matter to you.</p>
]]></content:encoded></item><item><title><![CDATA[Why I Started Writing a Blog as a Software Engineer]]></title><description><![CDATA[Hi, my name is Iriyanto, and I’ve been working as a Software Engineer for about five years now. My primary focus is on Backend Web Development, but from time to time, I also take on roles such as system analyst or even a project manager.
One of the m...]]></description><link>https://blog.iriyanto.com/why-i-started-writing-a-blog-as-a-software-engineer</link><guid isPermaLink="true">https://blog.iriyanto.com/why-i-started-writing-a-blog-as-a-software-engineer</guid><category><![CDATA[developer-journey]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Open Source Community]]></category><category><![CDATA[start blogging as a developer]]></category><category><![CDATA[Personal growth  ]]></category><dc:creator><![CDATA[Iriyanto]]></dc:creator><pubDate>Tue, 12 Aug 2025 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/gWjJsIGNnbY/upload/5095df5102009fbd79d2b1ff553d74d2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi, my name is <strong>Iriyanto</strong>, and I’ve been working as a <strong>Software Engineer</strong> for about <strong>five years</strong> now. My primary focus is on Backend Web Development, but from time to time, I also take on roles such as system analyst or even a project manager.</p>
<p>One of the main reasons I decided to start this blog is to improve my communication skills. Writing is a great way for me to practice putting ideas into words, and I believe this will also help me in the future when I start creating content on platforms like YouTube.</p>
<p>Another thing I’m really passionate about right now is <strong>open source</strong>. In Indonesia, it’s quite common for developers to contribute as collaborators to projects, especially those started abroad, but it’s rare to see local developers initiating and maintaining their own open source projects. That’s something I want to change. My long-term goal is to dedicate more time to open source and eventually start projects that come from Indonesia and hopefully make an impact.</p>
<p>Besides those bigger goals, this blog will also serve as a personal space for me. I’ll use it to document my learning journey, share project experiences, and build my portfolio and personal brand along the way.</p>
<p>That’s it for a short introduction—thanks for stopping by, and nice to meet you!</p>
]]></content:encoded></item></channel></rss>