W28: Secure Architecture, Orchestration, and Performance in ArceApps
Welcome to a new installment of the ArceApps devlog. As an independent developer (“Indie Spirit”), my focus has always been on building a solid and transparent technical foundation. Before diving into the technical details of this fortnight, it is crucial to make a fundamental distinction: this devlog exclusively documents the progress within the ecosystem and platform of the ArceApps Portfolio, completely separating it from my other developments like the PuzzleHub games. Here, we are discussing the core infrastructure, the artificial intelligence agents, and the web architecture that supports my entire digital presence.
Over the past two weeks, my engineering effort has been centered around three main pillars: security in DOM manipulation, a comprehensive modernization of the testing environment using Vitest, and critical optimizations in Astro’s build times. These changes not only improve the platform’s current robustness but also lay the groundwork for the future orchestration of autonomous agents I am developing. Throughout this article, I will detail my workflow, the architectural decisions I have made, and share real code snippets that illustrate how I tackled these challenges.
Milestone 1: Security in DOM Manipulation and JSON Serialization
One of the primary challenges in modern web development is ensuring that malicious code injections do not compromise the user experience. While auditing the architecture of the ArceApps Portfolio, I identified certain potential vulnerabilities related to how content was inserted into the DOM and how structured data (JSON-LD) was serialized for SEO.
Eliminating innerHTML
The first action was to completely eradicate the use of the innerHTML property across various client-side scripts. Relying on innerHTML is one of the most common vectors for Cross-Site Scripting (XSS) attacks, especially if the injected content originates from dynamic or untrusted sources. Even though my blog content is static and generated by me or my agents, practicing defense-in-depth demands that we utilize more secure DOM APIs.
In the script responsible for the code-copy functionality (src/scripts/code-copy.ts), I replaced direct HTML insertion with element creation using document.createElement, textContent, and replaceChildren. This approach ensures that any dynamic content is treated strictly as text, eliminating any possibility of the browser parsing and executing it as markup or scripts.
Secure Serialization with safeJsonLd
The second aspect of security involved injecting SEO metadata. In Astro, it’s common practice to include <script type="application/ld+json"> tags to provide search engines with structured data. However, if this data contains special characters that could prematurely close the script tag, a window opens for injection attacks.
To resolve this, I implemented a robust utility function named safeJsonLd in src/utils/security.ts. This function not only safely converts objects to JSON strings but also explicitly escapes context-sensitive HTML characters.
/**
* Safely serializes data to a JSON string for use in JSON-LD <script> tags.
* Escapes characters that could be used for script injection or that have
* special meaning in HTML, while preserving valid JSON.
*
* @param data The object to serialize
* @returns A safe JSON string
*/
export function safeJsonLd(data: unknown): string {
return JSON.stringify(data)
.replace(/</g, '\u003c')
.replace(/>/g, '\u003e')
.replace(/&/g, '\u0026')
.replace(/
/g, '\u2028')
.replace(/
/g, '\u2029');
}
As seen in the snippet, any occurrence of <, >, and & is converted to its Unicode representation. I also escape line and paragraph separators (
and
), which can cause SyntaxErrors in older JavaScript engines if included directly inside a string literal. This additional layer of security allows me to rest easy, knowing my web ecosystem is protected from the ground up.
Milestone 2: Evolution of the Testing Infrastructure with Vitest
Maintaining a complex ecosystem like the ArceApps Portfolio—which includes hundreds of dynamic pages and cross-cutting utilities—requires a foolproof safety net. During this fortnight, I decided to take a step forward in the project’s maturity by consolidating my testing infrastructure around Vitest.
JSDOM and Globals Configuration
The transition to Vitest was motivated by its exceptional speed and native integration with the Vite ecosystem, upon which Astro already heavily relies. I configured Vitest in vitest.config.ts to operate with a simulated browser environment (jsdom), which is indispensable for testing user interface scripts. Additionally, I enabled globals to streamline test writing.
/// <reference types="vitest" />
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
globals: true, // Allows using describe, it, expect without imports
include: ['src/**/*.{test,spec}.{js,ts}'],
},
});
This configuration allows my test scripts to focus exclusively on business logic without the visual clutter of importing basic functions. It was a transformative shift in my Test-Driven Development workflow, significantly boosting my productivity as a solopreneur.
Comprehensive Coverage in Utilities
Once the environment was set up, I dedicated considerable effort to increasing test coverage. Key files like slugs.ts, sanitizer.ts, and the newly created security.ts are now thoroughly tested. For example, in the search sanitizer, I ensured that Denial of Service (DoS) attacks via massive text inputs were mitigated by properly truncating string lengths while simultaneously purging residual HTML tags. These tests ensure that, regardless of future changes, the integrity of my tools remains intact.
Milestone 3: The Challenge of the Week - Optimizing the Astro Build
As the number of articles in the blog and devlog sections grew (including the massive comparative posts from the recent AI-driven IDEs tournament), I began experiencing increasingly long build times in Astro. Being a developer obsessed with efficiency, this was a challenge I decided to tackle head-on.
The Problem: Sequential Queries in getStaticPaths
Investigating the bottleneck, I discovered that in the templates responsible for generating dynamic routes, the code was calling the getCollection function inside iterators that didn’t exploit concurrency. Each call to fetch a collection depended on the previous one finishing, creating an unnecessary waiting pattern that scaled linearly with the amount of content. This drastically affected both local builds and continuous integration.
The Solution: Concurrency with Promise.all
The architectural solution lay in deeply refactoring the getStaticPaths functions. I modified the logic so that the various required collections (for instance, loading both the Spanish and English blogs simultaneously) were requested concurrently using Promise.all.
The impact was immediate and powerful. By parallelizing data loading, the total read time was reduced to the duration of the slowest operation, rather than the sum of all individual operations. Furthermore, I reviewed optimization techniques in Map iteration, substituting costly patterns with traditional for loops and leveraging Set data structures for in-memory lookups with a constant time complexity, $O(1)$. This iterative improvement, while subtle at the code level, achieved a measurable 5-10% reduction in certain heavy routines, validating the principle that details matter when scaling an ecosystem.
Reflections and Future Vision
Building the ArceApps Portfolio in public isn’t just about publishing code; it’s about sharing the journey of technical and strategic growth. Over these weeks, I have reinforced critical security areas, established a solid foundation for testing, and resolved scalability issues in static content generation. My technology stack feels much more resilient today than it did at the beginning of the month.
Looking ahead, the next two weeks promise to be equally exciting. With the content compression layer and security measures fully in place, the stage is set to advance in sophisticated automations. My focus will shift toward refining the CI/CD system, aiming to integrate more complex validations directly into pre-commit hooks and deepening the asynchronous communication among AI agents.
Every step I take in perfecting my “Indie Tech Stack” reaffirms my commitment to technical excellence. As a solopreneur, I don’t have a “QA department” or an “operations team”; I am fully responsible for the entire software lifecycle. And that is precisely what makes every technical victory—no matter how small, from correctly escaping an ampersand to shaving a few milliseconds off the Astro build—deeply satisfying.
Software architecture is a living, constantly iterating process. I will continue to share these learnings, challenges, and victories, consolidating ArceApps not just as a portfolio, but as a tangible testament to high-quality independent development. Until the next fortnight.