<?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[PSkinnerTech]]></title><description><![CDATA[Former Paratrooper Medic turned Software Engineer.
Lead DevRel - Arweave via Forward Research]]></description><link>https://blog.patrickskinner.tech</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1677173067244/yYPK5ZiGS.png</url><title>PSkinnerTech</title><link>https://blog.patrickskinner.tech</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 05:11:40 GMT</lastBuildDate><atom:link href="https://blog.patrickskinner.tech/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building Scalable Enterprise MCP Servers]]></title><description><![CDATA[Introduction
I've built several custom MCP servers, ranging from basic implementations to sophisticated setups capable of commanding satellite imaging through SkyFi's API using natural language. In this tutorial, I will share my in-depth development ...]]></description><link>https://blog.patrickskinner.tech/building-scalable-enterprise-mcp-servers</link><guid isPermaLink="true">https://blog.patrickskinner.tech/building-scalable-enterprise-mcp-servers</guid><category><![CDATA[mcp]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Python]]></category><category><![CDATA[enterprise]]></category><category><![CDATA[Enterprise AI]]></category><category><![CDATA[mcp server]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Sat, 26 Jul 2025 04:52:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753501429655/f3baf63b-87e9-458c-9b1d-40867b8e0a43.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>I've built several custom MCP servers, ranging from basic implementations to sophisticated setups capable of commanding satellite imaging through SkyFi's API using natural language. In this tutorial, I will share my in-depth development philosophy for creating production-ready, scalable MCP (Model Context Protocol) servers. We'll break down core concepts, detailed architecture, and essential best practices.</p>
<h2 id="heading-what-is-an-mcp-server">What is an MCP Server?</h2>
<p>An MCP server is a backend service designed to interact with various external APIs, manage complex authentication workflows, and provide structured data transformations. MCP servers facilitate natural language interactions by exposing modular, reusable "tools" to language models (LMs), enabling dynamic, secure, and efficient automation of sophisticated workflows.</p>
<h3 id="heading-why-use-an-mcp-server">Why Use an MCP Server?</h3>
<ul>
<li><strong>Simplified API Integration:</strong> Abstract complex external APIs into standardized, easy-to-consume tools.</li>
<li><strong>Scalability and Security:</strong> Layered architecture ensures scalability, maintainability, and robust security.</li>
<li><strong>Flexibility:</strong> Multi-method authentication and modular services provide flexibility for diverse enterprise environments.</li>
<li><strong>Enhanced User Experience:</strong> Intuitive, structured responses and comprehensive error handling greatly improve user interactions.</li>
</ul>
<p>If you want to read more about the original outline of what an MCP server is, by the very team that originally created the MCP server standard, check out this article from Anthropic: <a target="_blank" href="https://www.anthropic.com/news/model-context-protocol">https://www.anthropic.com/news/model-context-protocol</a></p>
<h2 id="heading-core-development-philosophy">Core Development Philosophy</h2>
<h3 id="heading-1-enterprise-first-developer-friendly">1. Enterprise-First, Developer-Friendly</h3>
<p>My MCP server approach prioritizes enterprise-grade scalability and robust security from inception, but it also ensures simplicity and clarity for developers working with basic use cases. We can embed complexity only where its needed while also giving developers the ability to easily engage with foundational functionality without losing the extensibility of advanced scenarios.</p>
<p><em>Not AI slop… I actually wrote that… 👆</em></p>
<p><strong>Implementation Strategies:</strong></p>
<ul>
<li><strong>Flexible Authentication:</strong> Support dual authentication modes, balancing simplicity through API key usage for straightforward integrations, alongside comprehensive OAuth 2.0 and SAML authentication mechanisms for secure, enterprise-wide deployments.</li>
<li><strong>Scalable Deployment:</strong> Offer deployment strategies that scale seamlessly from straightforward Docker containers for quick starts and prototyping to Kubernetes orchestrations designed for robust, high-availability production environments.</li>
<li><strong>Versatile Service Management:</strong> Accommodate both single-service instances for targeted applications and multi-tenant configurations suitable for large-scale, shared deployments.</li>
</ul>
<h3 id="heading-2-strict-layering-with-clear-boundaries">2. Strict Layering with Clear Boundaries</h3>
<p>The MCP server architecture is meticulously structured, with each layer clearly defined by distinct responsibilities, isolated interfaces, and explicit boundaries. This modular design promotes maintainability, simplifies troubleshooting, and ensures each component functions optimally within a cohesive system.</p>
<p><strong>Architectural Layers:</strong></p>
<ul>
<li><strong>MCP Transport Layer:</strong> Facilitates robust communication protocols, including standard IO (STDIO), Server-Sent Events (SSE), and streamable HTTP, ensuring reliable and efficient interactions.</li>
<li><strong>FastMCP Server Layer:</strong> Integrates core framework features, dynamically filtering and exposing the appropriate tools and capabilities. The word "FastMCP" definitely seems coined and branded, which kinda is. But, it is open source, so don't worry. You can dive into FastMCP <a target="_blank" href="https://pypi.org/project/fastmcp/1.0/">v1</a> and <a target="_blank" href="https://gofastmcp.com/getting-started/welcome">v2</a> in different approaches. Feel free to dive into them there.</li>
<li><strong>Service Layer:</strong> Encapsulates dedicated business logic, tailored specifically to the operational requirements of each service.</li>
<li><strong>Data Processing Layer:</strong> Handles data validation, transformation, and ensures structured, human-readable responses, enhancing data integrity and clarity.</li>
<li><strong>Authentication Layer:</strong> Implements rigorous multi-method authentication strategies to ensure secure access control tailored to diverse security demands.</li>
<li><strong>Network Layer:</strong> Manages efficient and optimized HTTP client interactions with external APIs, ensuring performance and reliability.</li>
</ul>
<h3 id="heading-3-independent-mountable-modules">3. Independent, Mountable Modules</h3>
<p>Every service within an MCP server ecosystem is structured as a self-contained, mountable module, promoting ease of deployment, maintenance, and scaling. This modular independence enhances operational agility, facilitates parallel development, and enables focused security management.</p>
<p><strong>Module Characteristics:</strong></p>
<ul>
<li><strong>Dedicated Configuration:</strong> Each service module features independent, environment-based configuration factories (<code>from_env</code>) for consistent and flexible setup.</li>
<li><strong>Robust Authentication Validation:</strong> Modules independently verify their authentication configurations (<code>is_auth_configured</code>), ensuring operational readiness and secure service access.</li>
<li><strong>Isolated Client Management:</strong> Each module maintains its own dedicated client with optimized connection pooling, ensuring efficient and resilient API communications.</li>
<li><strong>Clear Logging and Error Handling:</strong> Comprehensive and distinctly scoped logging and error handling namespaces are maintained for precise diagnostics and straightforward troubleshooting.</li>
</ul>
<h2 id="heading-why-this-mcp-approach-is-a-scalable-enterprise-model">Why this MCP Approach is a Scalable Enterprise Model</h2>
<p>This MCP server approach is a scalable enterprise model because it fundamentally incorporates clear separation of concerns, modularity, and extensibility from the very beginning. Unlike traditional monolithic systems, which become increasingly challenging to scale and maintain due to tightly coupled components and rigid architectures, …<em>(or just unguided AI slop)…</em> MCP's layered with a modular structure enables incremental scaling, targeted optimizations, and independent component upgrades. Also, its multi-method authentication strategy and well-defined interfaces ensure secure integrations within complex enterprise ecosystems, which tend to accommodate evolving security requirements and diverse deployment scenarios. Within other methods (or lack there of methodology), they often lack clear separation and modular architecture which often leads to limitations in scalability, maintainability, and adaptability.</p>
<h2 id="heading-project-structure-template">Project Structure Template</h2>
<p>A robust project structure is critical for maintainability, scalability, and clarity. Here's a detailed breakdown of the recommended project structure:</p>
<pre><code class="lang-plaintext">mcp-{service-name}/
├── src/                            # Source code root
│   └── mcp_{service_name}/
│       ├── __init__.py             # Main entry point and CLI
│       ├── exceptions.py           # Custom exception classes
│       ├── servers/                # Contains server classes, middleware, and lifecycle management
│       ├── {service_a}/            # Dedicated modules for service A
│       │   ├── __init__.py
│       │   ├── client.py           # API client for service A
│       │   ├── config.py           # Configuration model for service A
│       │   └── tools.py            # MCP tools for service A
│       ├── {service_b}/            # Dedicated modules for service B
│       │   └── [same structure]
│       ├── models/                 # Data models and validation schemas
│       ├── preprocessing/          # Data transformation and preprocessing utilities
│       └── utils/                  # Common utilities (logging, auth, networking)
├── tests/                          # Comprehensive testing suite
│   ├── unit/                       # Unit tests for isolated logic
│   ├── integration/                # Integration tests for combined components
│   └── fixtures/                   # Shared test data and mocks
├── docs/                           # Documentation, guides, and references
├── Dockerfile                      # Docker configuration for containerization
└── README.md                       # Project overview and quick start guide
</code></pre>
<h2 id="heading-detailed-server-implementation">Detailed Server Implementation</h2>
<h3 id="heading-main-server-class">Main Server Class</h3>
<p>The main server class acts as the central orchestrator. It is responsible for the server's lifecycle, configuring middleware, and, most importantly, dynamically filtering which tools are available based on the current context (e.g., user permissions, server configuration). This ensures that a language model is only exposed to the tools it is authorized and able to use.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any
<span class="hljs-keyword">from</span> fastmcp <span class="hljs-keyword">import</span> FastMCP, MCPTool
<span class="hljs-keyword">from</span> starlette.middleware <span class="hljs-keyword">import</span> Middleware
<span class="hljs-keyword">from</span> .context <span class="hljs-keyword">import</span> MainAppContext
<span class="hljs-keyword">from</span> .dependencies <span class="hljs-keyword">import</span> get_available_services
<span class="hljs-keyword">from</span> .{service_a} <span class="hljs-keyword">import</span> service_a_mcp

logger = logging.getLogger(<span class="hljs-string">"mcp-server.main"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> {<span class="hljs-title">ServiceName</span>}<span class="hljs-title">MCP</span>(<span class="hljs-params">FastMCP[MainAppContext]</span>):</span>
    <span class="hljs-string">"""
    Custom FastMCP server with multi-service support and dynamic tool filtering.
    This class overrides the default tool discovery to apply business logic.
    """</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_mcp_list_tools</span>(<span class="hljs-params">self</span>) -&gt; list[MCPTool]:</span>
        <span class="hljs-string">"""Filters and lists available tools based on application context."""</span>
        req_context = self._mcp_server.request_context
        <span class="hljs-keyword">if</span> req_context <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">or</span> req_context.lifespan_context <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            logger.warning(<span class="hljs-string">"Lifespan context not available, returning no tools."</span>)
            <span class="hljs-keyword">return</span> []

        app_context = req_context.lifespan_context.get(<span class="hljs-string">"app_lifespan_context"</span>)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> app_context:
            <span class="hljs-keyword">return</span> []

        all_tools = <span class="hljs-keyword">await</span> self.get_tools()
        filtered_tools = [
            tool_obj.to_mcp_tool(name=tool_name)
            <span class="hljs-keyword">for</span> tool_name, tool_obj <span class="hljs-keyword">in</span> all_tools.items()
            <span class="hljs-keyword">if</span> self._should_include_tool(tool_name, tool_obj, app_context)
        ]
        <span class="hljs-keyword">return</span> filtered_tools

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_should_include_tool</span>(<span class="hljs-params">self, tool_name: str, tool_obj: Any, context: MainAppContext</span>) -&gt; bool:</span>
        <span class="hljs-string">"""Applies multi-level filtering logic for tool inclusion."""</span>
        <span class="hljs-comment"># Filter 1: Exclude tools if they are explicitly disabled.</span>
        <span class="hljs-keyword">if</span> context.enabled_tools <span class="hljs-keyword">and</span> tool_name <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> context.enabled_tools:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

        <span class="hljs-comment"># Filter 2: Exclude tools with 'write' operations if in read-only mode.</span>
        <span class="hljs-keyword">if</span> context.read_only <span class="hljs-keyword">and</span> <span class="hljs-string">"write"</span> <span class="hljs-keyword">in</span> tool_obj.tags:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

        <span class="hljs-comment"># Filter 3: Exclude tools if their parent service is not configured.</span>
        tool_tags = tool_obj.tags
        <span class="hljs-keyword">for</span> service_name <span class="hljs-keyword">in</span> [<span class="hljs-string">"service_a"</span>, <span class="hljs-string">"service_b"</span>]:
            <span class="hljs-keyword">if</span> service_name <span class="hljs-keyword">in</span> tool_tags:
                <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> getattr(context, <span class="hljs-string">f"full_<span class="hljs-subst">{service_name}</span>_config"</span>):
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">http_app</span>(<span class="hljs-params">self, middleware=None</span>):</span>
        <span class="hljs-string">"""Defines a middleware pipeline for auth, logging, and request handling."""</span>
        <span class="hljs-keyword">from</span> .servers.middleware <span class="hljs-keyword">import</span> UserTokenMiddleware

        auth_middleware = Middleware(UserTokenMiddleware, mcp_server_ref=self)
        final_middleware = [auth_middleware]
        <span class="hljs-keyword">if</span> middleware:
            final_middleware.extend(middleware)

        <span class="hljs-keyword">return</span> super().http_app(middleware=final_middleware)
</code></pre>
<h3 id="heading-service-configuration">Service Configuration</h3>
<p>Each service module requires a detailed configuration class. Using a dataclass with a <code>from_env</code> class method allows for type-safe, validated configuration loaded securely from environment variables. This prevents misconfigurations and keeps secrets out of the codebase.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> __future__ <span class="hljs-keyword">import</span> annotations
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dataclasses <span class="hljs-keyword">import</span> dataclass
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Optional, List, Dict

<span class="hljs-meta">@dataclass</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> {<span class="hljs-title">ServiceName</span>}<span class="hljs-title">Config</span>:</span>
    <span class="hljs-string">"""Configuration for the {ServiceName} service, loaded from environment variables."""</span>
    url: str                                  <span class="hljs-comment"># Base URL of the service API</span>

    <span class="hljs-comment"># Authentication Methods (in order of precedence)</span>
    oauth_client_id: Optional[str] = <span class="hljs-literal">None</span>     <span class="hljs-comment"># OAuth client identifier</span>
    oauth_client_secret: Optional[str] = <span class="hljs-literal">None</span> <span class="hljs-comment"># OAuth client secret</span>
    personal_token: Optional[str] = <span class="hljs-literal">None</span>      <span class="hljs-comment"># Personal Access Token for enterprise</span>
    api_key: Optional[str] = <span class="hljs-literal">None</span>             <span class="hljs-comment"># Simple API key auth</span>

    <span class="hljs-comment"># Network settings</span>
    ssl_verify: bool = <span class="hljs-literal">True</span>
    timeout: int = <span class="hljs-number">30</span>

<span class="hljs-meta">    @classmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">from_env</span>(<span class="hljs-params">cls</span>) -&gt; {ServiceName}Config:</span>
        <span class="hljs-string">"""Constructs configuration from environment variables."""</span>
        <span class="hljs-keyword">return</span> cls(
            url=os.getenv(<span class="hljs-string">"{SERVICE_NAME}_URL"</span>, <span class="hljs-string">""</span>),
            oauth_client_id=os.getenv(<span class="hljs-string">"{SERVICE_NAME}_OAUTH_CLIENT_ID"</span>),
            oauth_client_secret=os.getenv(<span class="hljs-string">"{SERVICE_NAME}_OAUTH_CLIENT_SECRET"</span>),
            personal_token=os.getenv(<span class="hljs-string">"{SERVICE_NAME}_PERSONAL_TOKEN"</span>),
            api_key=os.getenv(<span class="hljs-string">"{SERVICE_NAME}_API_KEY"</span>),
            ssl_verify=os.getenv(<span class="hljs-string">"{SERVICE_NAME}_SSL_VERIFY"</span>, <span class="hljs-string">"true"</span>).lower() == <span class="hljs-string">"true"</span>,
            timeout=int(os.getenv(<span class="hljs-string">"{SERVICE_NAME}_TIMEOUT"</span>, <span class="hljs-string">"30"</span>)),
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_auth_configured</span>(<span class="hljs-params">self</span>) -&gt; bool:</span>
        <span class="hljs-string">"""Checks if any valid authentication method is configured."""</span>
        <span class="hljs-comment"># Checks for OAuth, PAT, or API Key credentials.</span>
        has_oauth = self.oauth_client_id <span class="hljs-keyword">and</span> self.oauth_client_secret
        has_pat = bool(self.personal_token)
        has_api_key = bool(self.api_key)
        <span class="hljs-keyword">return</span> has_oauth <span class="hljs-keyword">or</span> has_pat <span class="hljs-keyword">or</span> has_api_key
</code></pre>
<h3 id="heading-service-client">Service Client</h3>
<p>A robust client class is essential for interacting with external APIs. It should encapsulate all logic for making requests, including handling authentication, retries, and error management. This isolates network concerns from the business logic within the tools themselves.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> httpx
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any, Optional
<span class="hljs-keyword">from</span> .config <span class="hljs-keyword">import</span> {ServiceName}Config
<span class="hljs-keyword">from</span> ..exceptions <span class="hljs-keyword">import</span> {ServiceName}APIError, {ServiceName}AuthenticationError

logger = logging.getLogger(<span class="hljs-string">"mcp-server.{service_name}.client"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> {<span class="hljs-title">ServiceName</span>}<span class="hljs-title">Client</span>:</span>
    <span class="hljs-string">"""Asynchronous HTTP client for the {ServiceName} API."""</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, config: {ServiceName}Config, user_token: Optional[str] = None</span>):</span>
        self.config = config
        self.user_token = user_token
        self._client: Optional[httpx.AsyncClient] = <span class="hljs-literal">None</span>

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_ensure_client</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Initializes and configures the httpx.AsyncClient if not already present."""</span>
        <span class="hljs-keyword">if</span> self._client <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            headers = {<span class="hljs-string">"User-Agent"</span>: <span class="hljs-string">"MCP-{ServiceName}/1.0"</span>}
            <span class="hljs-comment"># Precedence: User-provided token &gt; Server-configured token</span>
            token = self.user_token <span class="hljs-keyword">or</span> self.config.personal_token
            <span class="hljs-keyword">if</span> token:
                headers[<span class="hljs-string">"Authorization"</span>] = <span class="hljs-string">f"Bearer <span class="hljs-subst">{token}</span>"</span>
            <span class="hljs-keyword">elif</span> self.config.api_key:
                headers[<span class="hljs-string">"X-API-Key"</span>] = self.config.api_key

            self._client = httpx.AsyncClient(
                base_url=self.config.url,
                headers=headers,
                verify=self.config.ssl_verify,
                timeout=self.config.timeout,
            )

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span>(<span class="hljs-params">self, endpoint: str, params: Optional[dict] = None</span>) -&gt; Any:</span>
        <span class="hljs-string">"""Executes an authenticated GET request with error handling."""</span>
        <span class="hljs-keyword">await</span> self._ensure_client()
        <span class="hljs-keyword">try</span>:
            response = <span class="hljs-keyword">await</span> self._client.get(endpoint, params=params)
            response.raise_for_status()
            <span class="hljs-keyword">return</span> response.json()
        <span class="hljs-keyword">except</span> httpx.HTTPStatusError <span class="hljs-keyword">as</span> e:
            <span class="hljs-keyword">if</span> e.response.status_code == <span class="hljs-number">401</span>:
                <span class="hljs-keyword">raise</span> {ServiceName}AuthenticationError(<span class="hljs-string">"Authentication failed."</span>)
            <span class="hljs-keyword">raise</span> {ServiceName}APIError(<span class="hljs-string">f"API Error: <span class="hljs-subst">{e.response.text}</span>"</span>)
        <span class="hljs-keyword">except</span> httpx.RequestError <span class="hljs-keyword">as</span> e:
            <span class="hljs-keyword">raise</span> {ServiceName}APIError(<span class="hljs-string">f"Network Error: <span class="hljs-subst">{str(e)}</span>"</span>)

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__aenter__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__aexit__</span>(<span class="hljs-params">self, exc_type, exc, tb</span>):</span>
        <span class="hljs-keyword">if</span> self._client:
            <span class="hljs-keyword">await</span> self._client.aclose()
</code></pre>
<h2 id="heading-authentication-architecture">Authentication Architecture</h2>
<h3 id="heading-multi-method-authentication">Multi-Method Authentication</h3>
<p>A scalable MCP server must support multiple authentication strategies to accommodate different environments. The architecture should follow a clear order of precedence, prioritizing the most secure and context-specific methods first.</p>
<ul>
<li><strong>Per-Request OAuth 2.0/PAT (Highest Priority):</strong> A token provided in the request header (<code>Authorization: Bearer ...</code>). This is ideal for multi-tenant systems where each user has their own credentials.</li>
<li><strong>Server-Configured Personal Access Token (PAT):</strong> A single token configured via environment variables, suitable for self-hosted or single-user enterprise environments.</li>
<li><strong>Server-Configured API Key/Token:</strong> A simpler static key for standard cloud integrations where OAuth is overkill.</li>
<li><strong>Basic Authentication (Legacy):</strong> Username and password, included for compatibility with older systems.</li>
</ul>
<h3 id="heading-authentication-middleware">Authentication Middleware</h3>
<p>Middleware is the component that intercepts every incoming request to extract and validate authentication tokens. It systematically checks headers for credentials and attaches them to the request's state, making them available to service clients and tools securely without passing them as function arguments.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> starlette.middleware.base <span class="hljs-keyword">import</span> BaseHTTPMiddleware, RequestResponseFunction
<span class="hljs-keyword">from</span> starlette.requests <span class="hljs-keyword">import</span> Request
<span class="hljs-keyword">from</span> starlette.responses <span class="hljs-keyword">import</span> Response

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserTokenMiddleware</span>(<span class="hljs-params">BaseHTTPMiddleware</span>):</span>
    <span class="hljs-string">"""
    Middleware to extract Bearer or API key tokens from request headers
    and attach them to the request state for per-request authentication.
    """</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">dispatch</span>(<span class="hljs-params">self, request: Request, call_next: RequestResponseFunction</span>) -&gt; Response:</span>
        auth_header = request.headers.get(<span class="hljs-string">"Authorization"</span>)
        api_key_header = request.headers.get(<span class="hljs-string">"X-API-Key"</span>)

        user_auth_token = <span class="hljs-literal">None</span>
        user_auth_type = <span class="hljs-literal">None</span>

        <span class="hljs-keyword">if</span> auth_header <span class="hljs-keyword">and</span> auth_header.startswith(<span class="hljs-string">"Bearer "</span>):
            user_auth_token = auth_header.split(<span class="hljs-string">" "</span>)[<span class="hljs-number">1</span>]
            user_auth_type = <span class="hljs-string">"bearer"</span>
        <span class="hljs-keyword">elif</span> api_key_header:
            user_auth_token = api_key_header
            user_auth_type = <span class="hljs-string">"api_key"</span>

        request.state.user_auth_token = user_auth_token
        request.state.user_auth_type = user_auth_type

        response = <span class="hljs-keyword">await</span> call_next(request)
        <span class="hljs-keyword">return</span> response
</code></pre>
<h2 id="heading-tool-development">Tool Development</h2>
<h3 id="heading-tool-organization">Tool Organization</h3>
<p>For clarity and maintainability, tools should be organized by feature domain, not by their technical operation (e.g., GET, POST). This makes the codebase intuitive and easier for developers to navigate.</p>
<pre><code class="lang-plaintext"># ✅ GOOD: Organization by feature domain
{service_name}/
├── projects.py       # Tools related to project management (create, get, list)
├── issues.py         # Tools for issue tracking (create, search, update)
└── users.py          # User management tools (get, invite)

# ❌ BAD: Organization by CRUD operation
{service_name}/
├── read.py           # Contains get_project, get_issue, get_user
├── create.py         # Contains create_project, create_issue
</code></pre>
<h3 id="heading-tool-definition-standards">Tool Definition Standards</h3>
<p>Every tool must be clearly defined with structured annotations, comprehensive docstrings, and consistent error handling. This metadata is what allows the language model to understand <em>how</em> and <em>when</em> to use the tool correctly.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Annotated
<span class="hljs-keyword">from</span> fastmcp <span class="hljs-keyword">import</span> MCPError, Context
<span class="hljs-keyword">from</span> .{service_name}.context <span class="hljs-keyword">import</span> {ServiceName}Context

<span class="hljs-meta">@{service}_mcp.tool(</span>
    name=<span class="hljs-string">"{service}_{feature}_{action}"</span>,
    description=<span class="hljs-string">"Performs a specific action on a feature within the service."</span>,
    tags=[<span class="hljs-string">"{service}"</span>, <span class="hljs-string">"read"</span>, <span class="hljs-string">"{feature_category}"</span>] <span class="hljs-comment"># Tags for filtering</span>
)
<span class="hljs-function"><span class="hljs-keyword">def</span> {<span class="hljs-title">feature</span>}<span class="hljs-title">_</span>{<span class="hljs-title">action</span>}<span class="hljs-title">_tool</span>(<span class="hljs-params">
    project_id: Annotated[str, <span class="hljs-string">"The unique identifier for the project. Example: 'PROJ-123'"</span>],
    context: Annotated[{ServiceName}Context, Context] <span class="hljs-comment"># Injects service-specific context</span>
</span>) -&gt; str:</span>
    <span class="hljs-string">"""
    Retrieves details for a specific project.

    Args:
        project_id: The ID of the project to retrieve.

    Returns:
        A formatted string containing the project details.

    Raises:
        MCPError: If the project is not found or authentication fails.
    """</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-comment"># Business logic is handled in the client</span>
        client = context.{service}_client
        project_data = client.get_project(project_id)

        <span class="hljs-comment"># Response formatting is handled by a dedicated utility</span>
        <span class="hljs-keyword">return</span> ResponseFormatter.format_item_response(project_data, item_type=<span class="hljs-string">"Project"</span>)

    <span class="hljs-keyword">except</span> {ServiceName}AuthenticationError <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">raise</span> MCPError(<span class="hljs-string">f"Authentication failed for <span class="hljs-subst">{context.service_name}</span>: <span class="hljs-subst">{e}</span>"</span>)
    <span class="hljs-keyword">except</span> {ServiceName}NotFoundError <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">raise</span> MCPError(<span class="hljs-string">f"Project not found: <span class="hljs-subst">{project_id}</span>"</span>)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        logger.error(<span class="hljs-string">f"Unexpected error in get_project_tool: <span class="hljs-subst">{e}</span>"</span>, exc_info=<span class="hljs-literal">True</span>)
        <span class="hljs-keyword">raise</span> MCPError(<span class="hljs-string">f"An unexpected error occurred: <span class="hljs-subst">{e}</span>"</span>)
</code></pre>
<h2 id="heading-data-processing-standards">Data Processing Standards</h2>
<p>Structured and human-readable responses are crucial for a good user experience. A dedicated formatting class ensures that all tools return data in a consistent, predictable, and clear manner. This greatly improves the language model's ability to interpret results and present them to the end-user.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResponseFormatter</span>:</span>
    <span class="hljs-string">"""A utility class for formatting API responses into human-readable strings."""</span>

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">format_item_response</span>(<span class="hljs-params">item: dict, item_type: str</span>) -&gt; str:</span>
        <span class="hljs-string">"""Formats a single dictionary item into a structured Markdown string."""</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> item:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{item_type}</span> not found."</span>

        lines = [
            <span class="hljs-string">f"# <span class="hljs-subst">{item_type}</span>: <span class="hljs-subst">{item.get(<span class="hljs-string">'name'</span>, item.get(<span class="hljs-string">'id'</span>, <span class="hljs-string">'N/A'</span>))}</span>"</span>,
            <span class="hljs-string">f"**ID**: `<span class="hljs-subst">{item.get(<span class="hljs-string">'id'</span>, <span class="hljs-string">'N/A'</span>)}</span>`"</span>,
            <span class="hljs-string">f"**Status**: <span class="hljs-subst">{item.get(<span class="hljs-string">'status'</span>, <span class="hljs-string">'N/A'</span>)}</span>"</span>,
            <span class="hljs-string">f"**Link**: <span class="hljs-subst">{item.get(<span class="hljs-string">'url'</span>, <span class="hljs-string">'No link available'</span>)}</span>"</span>,
            <span class="hljs-string">"\n## Description"</span>,
            item.get(<span class="hljs-string">'description'</span>, <span class="hljs-string">'No description provided.'</span>),
        ]
        <span class="hljs-keyword">return</span> <span class="hljs-string">"\n"</span>.join(lines)

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">format_list_response</span>(<span class="hljs-params">items: list[dict], item_type: str</span>) -&gt; str:</span>
        <span class="hljs-string">"""Formats a list of items into a summary table."""</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> items:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"No <span class="hljs-subst">{item_type}</span>s found."</span>

        headers = [<span class="hljs-string">"ID"</span>, <span class="hljs-string">"Name"</span>, <span class="hljs-string">"Status"</span>]
        rows = [[item.get(<span class="hljs-string">'id'</span>, <span class="hljs-string">'N/A'</span>), item.get(<span class="hljs-string">'name'</span>, <span class="hljs-string">'N/A'</span>), item.get(<span class="hljs-string">'status'</span>, <span class="hljs-string">'N/A'</span>)] <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> items]

        <span class="hljs-comment"># Simple text-based table generation</span>
        header_str = <span class="hljs-string">" | "</span>.join(headers)
        divider_str = <span class="hljs-string">" | "</span>.join([<span class="hljs-string">"---"</span>] * len(headers))
        row_strs = [<span class="hljs-string">" | "</span>.join(map(str, row)) <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> rows]

        <span class="hljs-keyword">return</span> <span class="hljs-string">"\n"</span>.join([header_str, divider_str] + row_strs)
</code></pre>
<h2 id="heading-environment-configuration">Environment Configuration</h2>
<p>Standardizing environment variable names is a simple but powerful practice for maintaining clarity and avoiding configuration errors, especially in complex deployments with multiple services.</p>
<pre><code class="lang-plaintext"># Convention: {SERVICE_NAME}_{COMPONENT}_{SETTING}
# Examples:
GITHUB_API_URL="https://api.github.com"
GITHUB_PERSONAL_TOKEN="ghp_xxxxxxxx"
GITHUB_TIMEOUT="45"

SLACK_API_TOKEN="xoxb-xxxxxxxx"
SLACK_SSL_VERIFY="false"
</code></pre>
<h2 id="heading-user-experience-optimization">User Experience Optimization</h2>
<h3 id="heading-progressive-disclosure-documentation">Progressive Disclosure Documentation</h3>
<p>Structure documentation to cater to different user needs, from a quick start for beginners to advanced guides for expert users. This approach prevents information overload and helps users find what they need quickly.</p>
<ul>
<li><strong>Quick Start (30 seconds):</strong> A minimal set of commands to get the server running with basic functionality.</li>
<li><strong>Standard Setup (5 minutes):</strong> Covers common production configurations, including API key authentication and Docker deployment.</li>
<li><strong>Advanced Configuration (15+ minutes):</strong> Details enterprise features like OAuth setup, multi-tenant deployments, and custom middleware.</li>
</ul>
<h3 id="heading-error-message-standards">Error Message Standards</h3>
<p>Provide error messages that are not only descriptive but also actionable. A good error message tells the user what went wrong, why it went wrong, and how to fix it.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ErrorMessageTemplates</span>:</span>
    AUTHENTICATION_ERROR = <span class="hljs-string">"""
    ❌ **Authentication Failed for {service_name}**

    **Cause:** The provided credentials (API Key or Token) are invalid or have insufficient permissions.
    **Solutions:**
    1. Verify your credentials in the `.env` file or request headers.
    2. Ensure the token has not expired and has the required scopes.
    3. Check the service's documentation for authentication help: {docs_url}
    """</span>

    CONFIGURATION_ERROR = <span class="hljs-string">"""
    ❌ **Configuration Error for {service_name}**

    **Missing Variable:** The `{missing_variable}` environment variable is not set.
    **To Fix:**
    - Set the variable in your terminal: `export {missing_variable}="value"`
    - Add it to your `.env` file: `{missing_variable}="value"`
    """</span>
</code></pre>
<h2 id="heading-testing-philosophy">Testing Philosophy</h2>
<h3 id="heading-comprehensive-testing">Comprehensive Testing</h3>
<p>A multi-layered testing strategy ensures reliability and stability. Each layer focuses on a different aspect of the system, from individual functions to the complete, integrated server.</p>
<pre><code class="lang-plaintext">tests/
├── unit/            # Tests for individual functions and classes in isolation.
│   ├── test_config.py
│   └── test_formatter.py
├── integration/     # Tests that verify interactions between components (e.g., client and a mock API).
│   └── test_service_a_client.py
└── mcp_protocol/    # End-to-end tests that check compliance with the MCP specification.
    └── test_tool_execution.py
└── fixtures/        # Reusable test data, mock responses, and helper factories.
    └── mock_api_responses.py
</code></pre>
<h3 id="heading-mocking-strategy">Mocking Strategy</h3>
<p>Use mock factories to produce consistent and reliable mock objects for your tests. This ensures that tests are repeatable and not dependent on the state of external services.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> unittest.mock <span class="hljs-keyword">import</span> Mock

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceMockFactory</span>:</span>
    <span class="hljs-string">"""Creates standardized mock objects for service clients for use in unit tests."""</span>

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_service_client_mock</span>(<span class="hljs-params">service_name: str, is_auth_ok: bool = True</span>):</span>
        <span class="hljs-string">"""Creates a mock service client with predefined success or failure responses."""</span>
        mock_client = Mock()

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> is_auth_ok:
            mock_client.get.side_effect = {ServiceName}AuthenticationError(<span class="hljs-string">"Invalid token"</span>)
            <span class="hljs-keyword">return</span> mock_client

        <span class="hljs-comment"># Configure standard successful responses</span>
        mock_client.get.return_value = {<span class="hljs-string">"id"</span>: <span class="hljs-string">"123"</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"Test Item"</span>}
        mock_client.post.return_value = {<span class="hljs-string">"id"</span>: <span class="hljs-string">"456"</span>, <span class="hljs-string">"status"</span>: <span class="hljs-string">"created"</span>}

        <span class="hljs-comment"># Configure common error scenarios</span>
        mock_client.get_not_found = Mock(side_effect={ServiceName}NotFoundError(<span class="hljs-string">"Item not found"</span>))

        <span class="hljs-keyword">return</span> mock_client
</code></pre>
<h2 id="heading-performance-and-scalability">Performance and Scalability</h2>
<h3 id="heading-caching-and-connection-pooling">Caching and Connection Pooling</h3>
<p>For high-throughput services, performance is critical. A properly configured HTTP client with effective connection pooling and caching reduces latency and minimizes the overhead of establishing new connections for every request.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> httpx

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_optimized_http_client</span>(<span class="hljs-params">base_url: str, verify: bool, timeout: int</span>) -&gt; httpx.AsyncClient:</span>
    <span class="hljs-string">"""
    Configures an optimized HTTP client with effective connection pooling
    and precise timeouts to ensure performance and reliability.
    """</span>
    <span class="hljs-comment"># Define connection limits to reuse connections efficiently</span>
    limits = httpx.Limits(
        max_keepalive_connections=<span class="hljs-number">20</span>,  <span class="hljs-comment"># Max idle connections to keep open</span>
        max_connections=<span class="hljs-number">100</span>,           <span class="hljs-comment"># Max total connections in the pool</span>
    )

    <span class="hljs-comment"># Configure granular timeouts</span>
    timeouts = httpx.Timeout(
        connect=<span class="hljs-number">5.0</span>,   <span class="hljs-comment"># Time to establish a connection</span>
        read=<span class="hljs-number">20.0</span>,     <span class="hljs-comment"># Time to wait for a chunk of the response</span>
        write=<span class="hljs-number">10.0</span>,    <span class="hljs-comment"># Time to wait for a chunk of the request to be sent</span>
    )

    <span class="hljs-keyword">return</span> httpx.AsyncClient(
        base_url=base_url,
        limits=limits,
        timeout=timeouts,
        verify=verify,
        http2=<span class="hljs-literal">True</span>,  <span class="hljs-comment"># Enable HTTP/2 for multiplexing if the server supports it</span>
    )
</code></pre>
<h2 id="heading-deployment-strategies">Deployment Strategies</h2>
<h3 id="heading-container-and-kubernetes-ready-design">Container and Kubernetes-Ready Design</h3>
<p>Design your application to be container-first using Docker. A multi-stage <code>Dockerfile</code> creates a small, secure, and efficient image. For scaling, use Kubernetes manifests to define deployments, services, and configurations, enabling automated scaling and high availability.</p>
<p><strong>Secure Dockerfile:</strong></p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Stage 1: Builder - Installs dependencies</span>
<span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.11</span>-slim AS builder
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install poetry</span>
<span class="hljs-keyword">COPY</span><span class="bash"> poetry.lock pyproject.toml ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> poetry install --no-root --no-dev</span>

<span class="hljs-comment"># Stage 2: Runtime - Creates the final, minimal image</span>
<span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.11</span>-slim
<span class="hljs-keyword">RUN</span><span class="bash"> useradd --create-home app</span>
<span class="hljs-keyword">USER</span> app
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /home/app</span>

<span class="hljs-comment"># Copy dependencies from the builder stage and source code</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/.venv ./.venv</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --chown=app:app src/ ./src/</span>

<span class="hljs-comment"># Set non-root user and define health check</span>
<span class="hljs-keyword">ENV</span> PATH=<span class="hljs-string">"/home/app/.venv/bin:$PATH"</span>
<span class="hljs-keyword">HEALTHCHECK</span><span class="bash"> --interval=30s --timeout=10s --retries=3 \
  CMD curl -f http://localhost:8000/healthz || <span class="hljs-built_in">exit</span> 1</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">8000</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"python"</span>, <span class="hljs-string">"-m"</span>, <span class="hljs-string">"src.mcp_{service_name}"</span>, <span class="hljs-string">"--transport"</span>, <span class="hljs-string">"streamable-http"</span>]</span>
</code></pre>
<p><strong>Kubernetes Deployment YAML:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">mcp-{service-name}</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">mcp-{service-name}</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">mcp-{service-name}</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mcp-{service-name}</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">your-registry/mcp-{service-name}:latest</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8000</span>
        <span class="hljs-attr">envFrom:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">configMapRef:</span>
            <span class="hljs-attr">name:</span> <span class="hljs-string">mcp-env-config</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">secretRef:</span>
            <span class="hljs-attr">name:</span> <span class="hljs-string">mcp-api-secrets</span>
        <span class="hljs-attr">livenessProbe:</span>
          <span class="hljs-attr">httpGet:</span>
            <span class="hljs-attr">path:</span> <span class="hljs-string">/healthz</span>
            <span class="hljs-attr">port:</span> <span class="hljs-number">8000</span>
          <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">30</span>
</code></pre>
<h2 id="heading-monitoring-and-observability">Monitoring and Observability</h2>
<h3 id="heading-structured-logging-and-metrics">Structured Logging and Metrics</h3>
<p>Implement structured logging (e.g., JSON format) from the start. It allows for powerful filtering and querying in modern observability platforms. This is essential for debugging issues in a distributed, containerized environment.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> structlog
<span class="hljs-keyword">import</span> sys

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">configure_logging</span>():</span>
    <span class="hljs-string">"""
    Sets up structured logging using structlog to output JSON logs.
    This enables easier parsing, searching, and analysis in production.
    """</span>
    structlog.configure(
        processors=[
            structlog.stdlib.add_log_level,
            structlog.processors.TimeStamper(fmt=<span class="hljs-string">"iso"</span>),
            structlog.processors.JSONRenderer(),
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
        cache_logger_on_first_use=<span class="hljs-literal">True</span>,
    )

    <span class="hljs-comment"># Example usage:</span>
    <span class="hljs-comment"># log = structlog.get_logger("my_app")</span>
    <span class="hljs-comment"># log.info("server_started", port=8000, transport="http")</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Though this write up is pretty brief in some areas, I hope it works as a guide for you—or your AI— to guide you to building scalable, secure, and user-friendly MCP servers. If you stick to these simple principles, you ensure your MCP server deployments will be capable of handling complex enterprise scenarios. Overall, I hope you enjoyed this guide. Feel free to hit me up on X if you have follow up questions... PEACE!!! ✌️ https://x.com/PSkinnerTech</p>
]]></content:encoded></item><item><title><![CDATA[React Native Camera: Expo vs VisionCamera — What You Need to Know]]></title><description><![CDATA[We're now in the world of AI development (aka vibe coding), and everyone is now wanting to deploy new apps that before they couldn't do to either time constraints or education limitations. I've built out a couple of mobile apps in the past, one I'm a...]]></description><link>https://blog.patrickskinner.tech/react-native-camera-expo-vs-visioncamera-what-you-need-to-know</link><guid isPermaLink="true">https://blog.patrickskinner.tech/react-native-camera-expo-vs-visioncamera-what-you-need-to-know</guid><category><![CDATA[React Native]]></category><category><![CDATA[Expo]]></category><category><![CDATA[Expo camera]]></category><category><![CDATA[Vision]]></category><category><![CDATA[vision camera]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Thu, 26 Jun 2025 18:07:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750960917268/4a2a5c04-efca-4d9e-a322-da78e0ce9c68.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We're now in the world of AI development (aka vibe coding), and everyone is now wanting to deploy new apps that before they couldn't do to either time constraints or education limitations. I've built out a couple of mobile apps in the past, one I'm actively working on to help train hockey players, and I learned very quickly about performance bottlenecks simply because I chose the wrong stack to build with.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">In this article, I'm specifically going to focus on React Native since so many developers will default to that because there's a larger amount of developers that innately know JavaScript. So keep that in mind before you start talking about building with Flutter or Swift.</div>
</div>

<p>When building mobile applications with <strong>React Native</strong>, accessing the camera is often a key requirement. Whether you're capturing photos, scanning barcodes, or running real-time AI inference on video frames, the choice of camera library can make or break your app’s performance.</p>
<p>There are two primary approaches:</p>
<ul>
<li><p>Use the <code>expo-camera</code> module (for managed workflow projects)</p>
</li>
<li><p>Use <code>react-native-vision-camera</code> (for bare workflow/native modules)</p>
</li>
</ul>
<p>In this article, we’ll break down the pros and cons of each option and help you decide which one is right for your project.</p>
<hr />
<h2 id="heading-quick-comparison">🧪 Quick Comparison</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td><code>expo-camera</code></td><td><code>react-native-vision-camera</code></td></tr>
</thead>
<tbody>
<tr>
<td>Setup Complexity</td><td>🟢 Very easy</td><td>🔴 Requires native module setup</td></tr>
<tr>
<td>Expo Go Compatibility</td><td>🟢 Yes</td><td>🔴 No (requires EAS build or bare workflow)</td></tr>
<tr>
<td>Real-Time Frame Access</td><td>🔴 Limited</td><td>🟢 Native-level fast access</td></tr>
<tr>
<td>Manual Camera Controls</td><td>🔴 Basic</td><td>🟢 Full (ISO, FPS, shutter, zoom, etc.)</td></tr>
<tr>
<td>Multi-Camera Support (ultrawide, depth)</td><td>🔴 No</td><td>🟢 Yes</td></tr>
<tr>
<td>ML/AI Processing Support</td><td>🔴 Poor</td><td>🟢 Excellent (via Frame Processors)</td></tr>
<tr>
<td>Ecosystem + Plugins</td><td>🔴 Limited</td><td>🟢 Robust (barcode, face detection, etc.)</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-option-1-expo-camera">🚀 Option 1: <code>expo-camera</code></h2>
<h3 id="heading-why-use-it">✅ Why Use It?</h3>
<ul>
<li><p>You're building a quick prototype or MVP</p>
</li>
<li><p>You want to stay inside the Expo Managed Workflow</p>
</li>
<li><p>Your needs are simple: take photos, record video, maybe scan a barcode</p>
</li>
</ul>
<h3 id="heading-why-avoid-it">❌ Why Avoid It?</h3>
<ul>
<li><p>You don’t get direct access to raw video frames</p>
</li>
<li><p>Advanced camera controls are absent</p>
</li>
<li><p>Real-time ML tasks (e.g., object detection, image classification) are slow or unsupported</p>
</li>
</ul>
<h3 id="heading-installation-expo">📦 Installation (Expo)</h3>
<pre><code class="lang-bash">npx expo install expo-camera
</code></pre>
<h3 id="heading-basic-usage">🔧 Basic Usage</h3>
<pre><code class="lang-plaintext">import { Camera } from 'expo-camera';

const MyCamera = () =&gt; {
  const [permission, requestPermission] = Camera.useCameraPermissions();

  if (!permission?.granted) {
    return &lt;Button onPress={requestPermission} title="Grant Camera Access" /&gt;;
  }

  return &lt;Camera style={{ flex: 1 }} /&gt;;
};
</code></pre>
<hr />
<h2 id="heading-option-2-react-native-vision-camera">🔥 Option 2: react-native-vision-camera</h2>
<p>If you want full control of the camera and need performance, this is the tool to use.</p>
<h3 id="heading-why-use-it-1">✅ Why Use It?</h3>
<ul>
<li><p>You need fast access to camera frames for AI or AR.</p>
</li>
<li><p>You want to integrate advanced features (HDR, frame rate control, manual focus)</p>
</li>
<li><p>You’re okay configuring native modules</p>
</li>
</ul>
<h3 id="heading-why-avoid-it-1">❌ Why Avoid It?</h3>
<ul>
<li><p>You need a managed Expo workflow</p>
</li>
<li><p>You’re avoiding native builds entirely</p>
</li>
</ul>
<h3 id="heading-installation">📦 Installation</h3>
<pre><code class="lang-bash">npm install react-native-vision-camera
npx pod-install
</code></pre>
<p><em>Note: You must also configure native permissions for Android and iOS, and request runtime permissions.</em></p>
<h3 id="heading-sample-code">⚙️ Sample Code</h3>
<pre><code class="lang-plaintext">import { Camera, useCameraDevices } from 'react-native-vision-camera';

const MyCamera = () =&gt; {
  const devices = useCameraDevices();
  const device = devices.back;

  if (device == null) return &lt;LoadingSpinner /&gt;;

  return (
    &lt;Camera
      style={{ flex: 1 }}
      device={device}
      isActive={true}
    /&gt;
  );
};
</code></pre>
<hr />
<h2 id="heading-visioncamera-frame-processors">🧠 VisionCamera Frame Processors</h2>
<p>This is where vision-camera really shines. You can tap into every video frame and run real-time tasks like face detection, QR scanning, or AI inference using TFLite or custom native modules.</p>
<pre><code class="lang-plaintext">const frameProcessor = useFrameProcessor((frame) =&gt; {
  'worklet';
  const faces = scanFaces(frame); // Native plugin
  console.log(faces);
}, []);
</code></pre>
<hr />
<h2 id="heading-technical-breakdown-why-visioncamera-is-superior">🧠 Technical Breakdown: Why VisionCamera Is Superior</h2>
<p>When performance, flexibility, or advanced use cases are involved, <code>react-native-vision-camera</code> clearly outperforms <code>expo-camera</code>. Here's why:</p>
<hr />
<h3 id="heading-1-frame-access-amp-native-performance">1. Frame Access &amp; Native Performance</h3>
<ul>
<li><p><code>expo-camera</code> limitation: Does <strong>not expose raw camera frames</strong> to JavaScript or native modules.</p>
<ul>
<li>Result: No real-time ML, AR, or video filters.</li>
</ul>
</li>
<li><p><code>react-native-vision-camera</code> advantage: Offers <strong>Frame Processors</strong>, written in <strong>C++/Java/Objective-C</strong> and executed on a <strong>dedicated thread</strong>, not the JS thread.</p>
<ul>
<li>Result: Run <strong>real-time computer vision tasks</strong> (e.g. object detection, barcode scanning) with <strong>low latency and zero dropped frames</strong>.</li>
</ul>
</li>
</ul>
<hr />
<h3 id="heading-2-direct-access-to-native-camera-apis">2. Direct Access to Native Camera APIs</h3>
<ul>
<li><p><strong>Expo</strong> wraps native APIs and only exposes what the team provides.</p>
</li>
<li><p><strong>VisionCamera</strong> interfaces <strong>directly with CameraX (Android) and AVCaptureSession (iOS)</strong>.</p>
<ul>
<li><p>You get:</p>
<ul>
<li><p>Full <strong>manual control</strong> over ISO, shutter speed, zoom, focus, white balance.</p>
</li>
<li><p>Support for <strong>ultra-wide lenses</strong>, <strong>telephoto</strong>, <strong>HDR</strong>, <strong>depth sensors</strong>.</p>
</li>
<li><p>Fine-grain access to <strong>stream formats</strong> like raw or YUV.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr />
<h3 id="heading-3-no-javascript-thread-bottlenecks">3. No JavaScript Thread Bottlenecks</h3>
<ul>
<li><p><code>expo-camera</code> sends preview frames and camera actions over the <strong>JS bridge</strong>:</p>
<ul>
<li>This introduces <strong>lag</strong> and <strong>frame drops</strong>.</li>
</ul>
</li>
<li><p><strong>VisionCamera</strong>:</p>
<ul>
<li><p>Uses <strong>GPU-backed native rendering</strong> for the preview.</p>
</li>
<li><p>Fully decouples camera logic from the JS thread.</p>
</li>
<li><p><strong>Preview stays fluid</strong> even while running compute-heavy tasks in parallel.</p>
</li>
</ul>
</li>
</ul>
<hr />
<h3 id="heading-4-plugin-ecosystem-amp-custom-native-extensions">4. Plugin Ecosystem &amp; Custom Native Extensions</h3>
<ul>
<li><p>VisionCamera supports a <strong>modular plugin system</strong>, enabling:</p>
<ul>
<li><p>Custom <strong>Frame Processors</strong> (e.g., scanFaces, detectBarcodes, trackMotion).</p>
</li>
<li><p>Direct integration with <strong>TensorFlow Lite</strong>, <strong>MediaPipe</strong>, or even <strong>OpenCV</strong>.</p>
</li>
<li><p>Native code execution inside the camera pipeline for <strong>peak performance</strong>.</p>
</li>
</ul>
</li>
</ul>
<hr />
<h3 id="heading-5-real-time-mlai-processing">5. Real-Time ML/AI Processing</h3>
<ul>
<li><p>AI camera apps demand <strong>low latency</strong>.</p>
</li>
<li><p><strong>With Expo:</strong></p>
<ul>
<li>Capture → JS bridge → process in JS → render = 🚫 too slow.</li>
</ul>
</li>
<li><p><strong>With VisionCamera:</strong></p>
<ul>
<li><p>Frames processed natively within <strong>2–5ms</strong>.</p>
</li>
<li><p>Consistent <strong>30–60 FPS inference</strong> supported.</p>
</li>
<li><p>Enables use cases like gesture detection, object recognition, pose estimation, etc.</p>
</li>
</ul>
</li>
</ul>
<hr />
<h2 id="heading-tldr-if-youre-doing-anything-advanced">🔧 TL;DR – If You're Doing Anything Advanced:</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Requirement</td><td><code>expo-camera</code></td><td><code>react-native-vision-camera</code></td></tr>
</thead>
<tbody>
<tr>
<td>Real-time ML/AI</td><td>🔴 No</td><td>🟢 Yes (via Frame Processors)</td></tr>
<tr>
<td>Manual camera control</td><td>🔴 Minimal</td><td>🟢 Full native control</td></tr>
<tr>
<td>High FPS (60+)</td><td>🔴 Limited</td><td>🟢 Supported</td></tr>
<tr>
<td>Multi-camera (e.g. ultrawide)</td><td>🔴 No</td><td>🟢 Yes</td></tr>
<tr>
<td>Native plugin support</td><td>🔴 None</td><td>🟢 Custom native extensions</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-conclusion">🚀 Conclusion</h2>
<p>If you need a basic camera for photos or videos and want fast setup, <code>expo-camera</code> is fine.</p>
<p>But for serious apps — especially those involving <strong>AI</strong>, <strong>AR</strong>, <strong>advanced camera controls</strong>, or <strong>high-performance UX</strong> — <code>react-native-vision-camera</code> is the right tool.</p>
<blockquote>
<p>⚠️ Just be ready to go beyond Expo Go and configure native builds — it's worth it.</p>
</blockquote>
<h2 id="heading-final-recommendation">💡 Final Recommendation</h2>
<p>Use Case Library Simple photo/video features, fast setup expo-camera Real-time ML, AR, advanced controls needed react-native-vision-camera Need to stay inside Expo Go expo-camera Willing to eject for better performance react-native-vision-camera</p>
<hr />
<h2 id="heading-wrap-up">🧱 Wrap-Up</h2>
<p>This is obviously written specifically for those that are working on an active project in Gauntlet AI Cohort 2. If any of you have any questions on this, feel free to hmu.</p>
<hr />
<h2 id="heading-helpful-links">🧰 Helpful Links</h2>
<ul>
<li><p><a target="_blank" href="https://github.com/mrousavy/react-native-vision-camera">VisionCamera GitHub</a></p>
</li>
<li><p><a target="_blank" href="https://docs.expo.dev/versions/latest/sdk/camera/">Expo Camera Docs</a></p>
</li>
<li><p><a target="_blank" href="https://react-native-vision-camera.com/docs/guides/frame-processors">VisionCamera Frame Processor Docs</a></p>
</li>
</ul>
<hr />
<p><em>Let me know if you want to dive deeper into performance benchmarks or set up a real-time ML example using VisionCamera + TensorFlow Lite!</em></p>
]]></content:encoded></item><item><title><![CDATA[Setting Up Supabase MCP Server with Cursor (With Debugging)]]></title><description><![CDATA[I Had a hell of a time getting my Supabase MCP Server working.  I figured that, because I went through the ringer on figuring out how to fix it, that I would share with you all that are probably googling how to get it fixed a guide to do just that.
I...]]></description><link>https://blog.patrickskinner.tech/setting-up-supabase-mcp-server-with-cursor-with-debugging</link><guid isPermaLink="true">https://blog.patrickskinner.tech/setting-up-supabase-mcp-server-with-cursor-with-debugging</guid><category><![CDATA[supabase]]></category><category><![CDATA[cursor]]></category><category><![CDATA[cursor ai]]></category><category><![CDATA[cursor IDE]]></category><category><![CDATA[mcp]]></category><category><![CDATA[mcp server]]></category><category><![CDATA[MCP Client]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Fri, 09 May 2025 22:26:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746829350718/4307270a-7b97-4684-bbd3-6b360bd65455.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I Had a hell of a time getting my Supabase MCP Server working.  I figured that, because I went through the ringer on figuring out how to fix it, that I would share with you all that are probably googling how to get it fixed a guide to do just that.</p>
<p>If you're unaware, the current official MCP server for Supabase seems to not be working very well at the moment: https://github.com/supabase-community/supabase-mcp/issues/28</p>
<h1 id="heading-setting-up-supabase-mcp-server-with-cursor">Setting Up Supabase MCP Server with Cursor</h1>
<p>This guide walks through setting up the Supabase Model Context Protocol (MCP) server with Cursor, allowing you to interact with your Supabase database directly through Cursor's AI assistant.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>A Supabase account with a project</li>
<li>Cursor IDE installed</li>
<li>Node.js installed (v18+ recommended)</li>
<li>A Supabase Personal Access Token (PAT)</li>
</ul>
<h2 id="heading-step-1-generate-a-supabase-personal-access-token">Step 1: Generate a Supabase Personal Access Token</h2>
<ol>
<li>Log in to your Supabase account at https://supabase.com/dashboard</li>
<li>Click on your profile icon in the top right corner</li>
<li>Select "Access Tokens" from the dropdown menu</li>
<li>Click "Generate new token"</li>
<li>Give your token a name (e.g., "Cursor MCP")</li>
<li>Copy the generated token (it starts with <code>sbp_</code>)</li>
</ol>
<h2 id="heading-step-2-install-required-packages">Step 2: Install Required Packages</h2>
<p>The Supabase MCP server requires specific packages to work properly. Install them locally in a dedicated directory:</p>
<h3 id="heading-mac">Mac</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a directory for MCP packages</span>
mkdir -p ~/.cursor/mcp-packages

<span class="hljs-comment"># Navigate to the directory</span>
<span class="hljs-built_in">cd</span> ~/.cursor/mcp-packages

<span class="hljs-comment"># Initialize a package.json</span>
npm init -y

<span class="hljs-comment"># Install required packages</span>
npm install @supabase/mcp-server-supabase @modelcontextprotocol/sdk
</code></pre>
<h3 id="heading-windows">Windows</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a directory for MCP packages</span>
mkdir -p %APPDATA%\Cursor\mcp-packages

<span class="hljs-comment"># Navigate to the directory</span>
<span class="hljs-built_in">cd</span> %APPDATA%\Cursor\mcp-packages

<span class="hljs-comment"># Initialize a package.json</span>
npm init -y

<span class="hljs-comment"># Install required packages</span>
npm install @supabase/mcp-server-supabase @modelcontextprotocol/sdk
</code></pre>
<h2 id="heading-step-3-configure-cursors-mcp-server">Step 3: Configure Cursor's MCP Server</h2>
<h3 id="heading-mac-setup">Mac Setup</h3>
<ol>
<li>Create or edit your Cursor MCP configuration file:</li>
</ol>
<pre><code class="lang-bash">mkdir -p ~/.cursor
touch ~/.cursor/mcp.json
</code></pre>
<ol start="2">
<li>Add the Supabase MCP server configuration:</li>
</ol>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"supabase"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"node"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"/Users/YOUR_USERNAME/.cursor/mcp-packages/node_modules/@supabase/mcp-server-supabase/dist/stdio.js"</span>,
        <span class="hljs-string">"--access-token"</span>,
        <span class="hljs-string">"YOUR_SUPABASE_PAT_HERE"</span>
      ]
    }
  }
}
</code></pre>
<ol start="3">
<li>Replace:<ul>
<li><code>YOUR_USERNAME</code> with your Mac username</li>
<li><code>YOUR_SUPABASE_PAT_HERE</code> with your Supabase Personal Access Token</li>
</ul>
</li>
</ol>
<h3 id="heading-windows-setup">Windows Setup</h3>
<ol>
<li><p>Create or edit your Cursor MCP configuration file at <code>%APPDATA%\Cursor\mcp.json</code></p>
</li>
<li><p>Add the Supabase MCP server configuration:</p>
</li>
</ol>
<h4 id="heading-option-1-using-npx-simpler-but-might-have-dependency-issues">Option 1: Using npx (simpler but might have dependency issues)</h4>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"supabase"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"cmd"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"/c"</span>,
        <span class="hljs-string">"npx"</span>,
        <span class="hljs-string">"-y"</span>,
        <span class="hljs-string">"@supabase/mcp-server-supabase"</span>,
        <span class="hljs-string">"--access-token"</span>,
        <span class="hljs-string">"YOUR_SUPABASE_PAT_HERE"</span>
      ]
    }
  }
}
</code></pre>
<p>Note: We're intentionally omitting <code>@latest</code> to avoid potential dependency conflicts.</p>
<h4 id="heading-option-2-using-locally-installed-packages-more-reliable">Option 2: Using locally installed packages (more reliable)</h4>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"supabase"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"cmd"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"/c"</span>,
        <span class="hljs-string">"node"</span>,
        <span class="hljs-string">"%APPDATA%\\Cursor\\mcp-packages\\node_modules\\@supabase\\mcp-server-supabase\\dist\\stdio.js"</span>,
        <span class="hljs-string">"--access-token"</span>,
        <span class="hljs-string">"YOUR_SUPABASE_PAT_HERE"</span>
      ]
    }
  }
}
</code></pre>
<ol start="3">
<li>Replace <code>YOUR_SUPABASE_PAT_HERE</code> with your Supabase Personal Access Token</li>
</ol>
<h2 id="heading-step-4-verify-the-mcp-server-path">Step 4: Verify the MCP Server Path</h2>
<h3 id="heading-mac-1">Mac</h3>
<ol>
<li>Make sure the path to the stdio.js file is correct:</li>
</ol>
<pre><code class="lang-bash">ls -la ~/.cursor/mcp-packages/node_modules/@supabase/mcp-server-supabase/dist/
</code></pre>
<ol start="2">
<li>Confirm that <code>stdio.js</code> exists in this directory and is executable.</li>
</ol>
<h3 id="heading-windows-1">Windows</h3>
<ol>
<li>Make sure the path to the stdio.js file is correct:</li>
</ol>
<pre><code class="lang-bash">dir %APPDATA%\Cursor\mcp-packages\node_modules\@supabase\mcp-server-supabase\dist\
</code></pre>
<ol start="2">
<li>Confirm that <code>stdio.js</code> exists in this directory.</li>
</ol>
<h2 id="heading-step-5-restart-cursor">Step 5: Restart Cursor</h2>
<ol>
<li>Completely close Cursor</li>
<li>Reopen Cursor</li>
<li>The Supabase MCP server should now be available</li>
</ol>
<h2 id="heading-security-considerations-and-docker-alternative">Security Considerations and Docker Alternative</h2>
<p>The setup described above runs the MCP server directly on your system, which has some security implications:</p>
<h3 id="heading-potential-vulnerabilities">Potential Vulnerabilities</h3>
<ol>
<li><strong>Access Token Exposure</strong>: Your Supabase PAT is stored in a plaintext file on your system</li>
<li><strong>Dependency Security</strong>: The MCP server and its dependencies run with your user permissions</li>
<li><strong>Network Access</strong>: The server has direct network access to Supabase from your machine</li>
</ol>
<h3 id="heading-docker-based-alternative-more-secure">Docker-Based Alternative (More Secure)</h3>
<p>Using Docker provides better isolation and security. Here's how to set it up:</p>
<h4 id="heading-1-create-a-dockerfile">1. Create a Dockerfile</h4>
<p>Create a file named <code>Dockerfile</code> in a directory of your choice:</p>
<pre><code class="lang-Dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-slim

<span class="hljs-keyword">RUN</span><span class="bash"> npm install -g @supabase/mcp-server-supabase @modelcontextprotocol/sdk</span>

<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> [<span class="hljs-string">"supabase-mcp"</span>, <span class="hljs-string">"--access-token"</span>]</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"YOUR_SUPABASE_PAT_HERE"</span>]</span>
</code></pre>
<p>Replace <code>YOUR_SUPABASE_PAT_HERE</code> with your Supabase PAT.</p>
<h4 id="heading-2-build-the-docker-image">2. Build the Docker image</h4>
<pre><code class="lang-bash">docker build -t supabase-mcp .
</code></pre>
<h4 id="heading-3-update-your-cursor-mcp-configuration">3. Update your Cursor MCP configuration</h4>
<h5 id="heading-maclinux">Mac/Linux</h5>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"supabase"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"docker"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"run"</span>,
        <span class="hljs-string">"-i"</span>,
        <span class="hljs-string">"--rm"</span>,
        <span class="hljs-string">"supabase-mcp"</span>
      ]
    }
  }
}
</code></pre>
<h5 id="heading-windows-2">Windows</h5>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"supabase"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"cmd"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"/c"</span>,
        <span class="hljs-string">"docker"</span>,
        <span class="hljs-string">"run"</span>,
        <span class="hljs-string">"-i"</span>,
        <span class="hljs-string">"--rm"</span>,
        <span class="hljs-string">"supabase-mcp"</span>
      ]
    }
  }
}
</code></pre>
<h3 id="heading-security-best-practices">Security Best Practices</h3>
<p>Regardless of which approach you choose:</p>
<ol>
<li><strong>Limit Token Permissions</strong>: Create a Supabase PAT with the minimum necessary permissions</li>
<li><strong>Regular Updates</strong>: Keep the MCP server and dependencies updated</li>
<li><strong>Monitor Access</strong>: Regularly audit your Supabase logs for unusual activity</li>
<li><strong>Token Rotation</strong>: Periodically rotate your PAT for enhanced security</li>
<li><strong>Local Network</strong>: Consider running the MCP server on a trusted local network only</li>
</ol>
<h2 id="heading-troubleshooting">Troubleshooting</h2>
<h3 id="heading-error-cannot-find-module">Error: Cannot find module</h3>
<p>If you see an error message like:</p>
<pre><code><span class="hljs-built_in">Error</span> [ERR_MODULE_NOT_FOUND]: Cannot find <span class="hljs-built_in">module</span> <span class="hljs-string">'@modelcontextprotocol/sdk/dist/esm/server/stdio.js'</span>
</code></pre><p>Try the following solutions (in order of simplicity):</p>
<ol>
<li><p><strong>Remove <code>@latest</code> from npx command</strong>: </p>
<ul>
<li>If you're using the npx approach with <code>@latest</code> suffix, remove it:<pre><code class="lang-json">{
<span class="hljs-attr">"mcpServers"</span>: {
  <span class="hljs-attr">"supabase"</span>: {
    <span class="hljs-attr">"command"</span>: <span class="hljs-string">"npx"</span>,
    <span class="hljs-attr">"args"</span>: [
      <span class="hljs-string">"-y"</span>,
      <span class="hljs-string">"@supabase/mcp-server-supabase"</span>,
      <span class="hljs-string">"--access-token"</span>,
      <span class="hljs-string">"YOUR_SUPABASE_PAT_HERE"</span>
    ]
  }
}
}
</code></pre>
</li>
<li>This simple change often resolves dependency conflicts</li>
</ul>
</li>
<li><p><strong>Use a direct local installation</strong>:</p>
<ul>
<li>Make sure you've installed the packages in the correct directory</li>
<li>Check that you're using the absolute path in your configuration</li>
<li>Point directly to the stdio.js file</li>
</ul>
</li>
<li><p><strong>Try global installation</strong>:</p>
<pre><code class="lang-bash">npm install -g @supabase/mcp-server-supabase @modelcontextprotocol/sdk
</code></pre>
</li>
<li><p><strong>For Windows users</strong>:</p>
<ul>
<li>Check that you're using the correct path with double backslashes in your configuration</li>
<li>If using the npx method, try switching to the local installation method</li>
</ul>
</li>
</ol>
<h3 id="heading-configuration-not-working">Configuration not working</h3>
<ol>
<li>Check your JSON syntax in <code>mcp.json</code> (no trailing commas, proper nesting)</li>
<li>Verify that your PAT is valid and not expired</li>
<li>Ensure you're pointing to the correct file (<code>stdio.js</code>, not <code>cli.js</code>)</li>
<li>On Windows, make sure paths use double backslashes (<code>\\</code>) in the JSON configuration</li>
</ol>
<h2 id="heading-using-the-supabase-mcp-server">Using the Supabase MCP Server</h2>
<p>Once configured, you can interact with your Supabase database through Cursor's AI assistant:</p>
<ol>
<li>Ask for information about your Supabase projects</li>
<li>Query your database tables</li>
<li>Insert, update, or delete data</li>
<li>Work with Supabase storage, authentication, and functions</li>
</ol>
<p>Example prompts:</p>
<ul>
<li>"Show me the schema of my Supabase database"</li>
<li>"List all users in my Supabase project"</li>
<li>"Create a new table in my Supabase database"</li>
<li>"Query all documents created in the last week"</li>
</ul>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><a target="_blank" href="https://supabase.com/docs/guides/getting-started/mcp">Supabase MCP Documentation</a></li>
<li><a target="_blank" href="https://docs.cursor.com/context/model-context-protocol">Cursor MCP Documentation</a></li>
<li><a target="_blank" href="https://www.anthropic.com/news/model-context-protocol">Model Context Protocol</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[ArDrive Turbo CLI Tutorial: Topping Up with ARIO Tokens]]></title><description><![CDATA[This tutorial walks through the process of using the ArDrive Turbo CLI to top up your account with ARIO tokens. It's an extremely simple process and makes it very easy to manage your Turbo Credit balance without ever leaving your terminal or your IDE...]]></description><link>https://blog.patrickskinner.tech/ardrive-turbo-cli-tutorial-topping-up-with-ario-tokens</link><guid isPermaLink="true">https://blog.patrickskinner.tech/ardrive-turbo-cli-tutorial-topping-up-with-ario-tokens</guid><category><![CDATA[ardrive]]></category><category><![CDATA[Arweave]]></category><category><![CDATA[ar.io]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Thu, 08 May 2025 20:11:32 GMT</pubDate><content:encoded><![CDATA[<p>This tutorial walks through the process of using the ArDrive Turbo CLI to top up your account with ARIO tokens. It's an extremely simple process and makes it very easy to manage your Turbo Credit balance without ever leaving your terminal or your IDE.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Node.js installed on your system</li>
<li>ArDrive CLI installed</li>
<li>A wallet file (JSON format)</li>
<li>ARIO tokens in your wallet</li>
<li><strong>Important</strong>: Make sure your <code>wallet.json</code> file is located in the same directory where you're running the commands</li>
</ul>
<h2 id="heading-step-1-install-turbo-sdk-globally">Step 1: Install Turbo SDK globally</h2>
<pre><code class="lang-bash">npm install -g @ardrive/turbo-sdk
</code></pre>
<h2 id="heading-step-2-check-your-current-turbo-balance">Step 2: Check your current Turbo balance</h2>
<pre><code class="lang-bash">turbo balance --wallet-file wallet.json
</code></pre>
<p>You can specifically check your ARIO-related balance:</p>
<pre><code class="lang-bash">turbo balance --wallet-file wallet.json --token ario
</code></pre>
<h2 id="heading-step-3-top-up-your-turbo-account-with-ario-tokens">Step 3: Top up your Turbo account with ARIO tokens</h2>
<p>Use the <code>crypto-fund</code> command to top up your account:</p>
<pre><code class="lang-bash">turbo crypto-fund --wallet-file wallet.json --token ario --value 100
</code></pre>
<p>Where:</p>
<ul>
<li><code>--wallet-file</code> specifies your wallet file</li>
<li><code>--token ario</code> indicates you want to use ARIO tokens</li>
<li><code>--value 100</code> is the amount of ARIO tokens to use (adjust as needed)</li>
</ul>
<p>You'll be asked to confirm the transaction. Type <code>yes</code> to proceed.</p>
<h2 id="heading-step-4-verify-your-updated-balance">Step 4: Verify your updated balance</h2>
<p>After the transaction is confirmed, check your balance again:</p>
<pre><code class="lang-bash">turbo balance --wallet-file wallet.json
</code></pre>
<h2 id="heading-using-turbo-credits-for-uploads">Using Turbo Credits for Uploads</h2>
<p>Now that you have Turbo credits, you can use them for faster uploads:</p>
<pre><code class="lang-bash">ardrive upload-file --wallet-file wallet.json --parent-folder-id YOUR_FOLDER_ID --local-path YOUR_FILE_PATH --turbo
</code></pre>
<h2 id="heading-troubleshooting">Troubleshooting</h2>
<ul>
<li>If you see "Destination address is not a valid supported native wallet address!" when using <code>top-up</code>, use <code>crypto-fund</code> instead.</li>
<li>Make sure your wallet has sufficient ARIO tokens before attempting to fund.</li>
<li>The credit amount received depends on the current exchange rate between ARIO and Turbo credits.</li>
<li>If you get file not found errors, verify that your <code>wallet.json</code> is in the current directory or provide the full path to the wallet file.</li>
</ul>
<h2 id="heading-additional-commands">Additional Commands</h2>
<p>View all available Turbo commands:</p>
<pre><code class="lang-bash">turbo --<span class="hljs-built_in">help</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[METT-TC for Developer Relations]]></title><description><![CDATA[Introduction

“If you find yourself in a fair fight, you didn’t plan accordingly.”— Colonel David Hackworth

This is by far my favorite quote. I heard this quote for the first time around 18 years ago while I was in RIP (Ranger Indoctrination Program...]]></description><link>https://blog.patrickskinner.tech/mett-tc-for-developer-relations</link><guid isPermaLink="true">https://blog.patrickskinner.tech/mett-tc-for-developer-relations</guid><category><![CDATA[mett-tc]]></category><category><![CDATA[army ranger]]></category><category><![CDATA[Patrick Skinner]]></category><category><![CDATA[#ranger]]></category><category><![CDATA[developer relations]]></category><category><![CDATA[DevRel]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Thu, 27 Feb 2025 20:17:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740687110141/2774acd8-649b-41a2-a396-f097043ecd24.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction"><strong>Introduction</strong></h1>
<blockquote>
<p><em>“If you find yourself in a fair fight, you didn’t plan accordingly.”</em><br />— Colonel David Hackworth</p>
</blockquote>
<p>This is by far my favorite quote. I heard this quote for the first time around 18 years ago while I was in RIP (Ranger Indoctrination Program) while serving in the US Army. It became an anthem that I repeat in my head while I planned for pretty much anything moving forward.</p>
<p>It's because of this quote I became more and more meticulous in how I planned things. I took one more thing from my time with the US Army Rangers that I began applying to my professional life. It's a strategy used for mission planning. You can actually read about it in detail in the <a target="_blank" href="https://www.milsci.ucsb.edu/sites/secure.lsit.ucsb.edu.mili.d7/files/sitefiles/resources/Ranger%20Handbook.pdf">Ranger Handbook</a>.</p>
<p>It's called <strong>METT-TC</strong>. Despite the fact that it's original intention is for combat mission planning, it's philosophy is easy to put into practice for pretty much anything, ESPECIALLY Developer Relations.</p>
<p>This article, we're going to explore how the military planning tool METT-TC can be adapted to craft robust DevRel strategies. We’ll <strong><em>delve</em></strong> into each component of METT-TC, redefine it for our context, and provide actionable insights on how to apply it to your DevRel initiatives.</p>
<h2 id="heading-the-importance-of-strategic-planning-in-devrel"><strong>The Importance of Strategic Planning in DevRel</strong></h2>
<p>Developer Relations is more than just outreach and retention; it’s a strategic role that <a target="_blank" href="https://nader.substack.com/p/building-high-impact-developer-communities">bridges</a> the gap between developers and the platform. A well-planned DevRel strategy ensures that developers are not only aware of your platform but are also engaged, productive, and advocates for your technology. There is so much that goes on within the Developer Relations role. The biggest mistake is going into the role without a meticulous plan in place.</p>
<h1 id="heading-introducing-mett-tc"><strong>Introducing METT-TC</strong></h1>
<p>METT-TC is an acronym that's used by the U.S. Army Rangers for mission planning. It's originally means is: <strong>Mission, Enemy, Terrain and Weather, Troops and Support Available, Time Available, Civilian Considerations.</strong> It's intended to know everything you could possibly know before going into a mission. Army Ranger missions are extremely high risk, so the more information you could know before going into a mission to make sure everyone comes back safe, the better.</p>
<p>Though Developer Relations may not seem early as serious, the lack of meticulous planning can be catastrophic to a startup's runway. Which is why I've applied the framework of METT-C to developer relations. The framework’s emphasis on thorough analysis and planning makes it highly adaptable allowing DevRels to thoroughly work through their strategy. In the end, this strategy mitigates unnecessary spending, time wasted, and DevRel burnout.</p>
<h2 id="heading-understanding-mett-tc-in-a-devrel-context"><strong>Understanding METT-TC in a DevRel Context</strong></h2>
<h3 id="heading-original-military-definitions"><strong>Original Military Definitions</strong></h3>
<p><em>Before we adapt METT-TC, let’s understand its original components that the Army Rangers use:</em></p>
<ul>
<li><p><strong>Mission:</strong> The task and its purpose.</p>
</li>
<li><p><strong>Enemy:</strong> The adversary’s capabilities and intentions.</p>
</li>
<li><p><strong>Terrain and Weather:</strong> Physical environment factors affecting the mission.</p>
</li>
<li><p><strong>Troops and Support Available:</strong> Friendly forces and resources.</p>
</li>
<li><p><strong>Time Available:</strong> Time constraints for planning and execution.</p>
</li>
<li><p><strong>Civilian Considerations:</strong> Impact on and from the civilian population.</p>
</li>
</ul>
<h3 id="heading-adapted-definitions-for-devrel"><strong>Adapted Definitions for DevRel</strong></h3>
<p><em>To make METT-TC relevant for DevRel, we’ll reinterpret each component:</em></p>
<ul>
<li><p><strong>Mission:</strong> The DevRel objectives—who, what, when, where, and why.</p>
</li>
<li><p><strong><s>Enemy</s> Ecosystem:</strong> The broader technological environment, including competitors and industry trends.</p>
</li>
<li><p><strong><s>Terrain and Weather</s> Target Market:</strong> The developers we aim to attract and their characteristics.</p>
</li>
<li><p><strong>Troops and Support Available:</strong> Internal teams and external resources at our disposal.</p>
</li>
<li><p><strong>Time Available:</strong> Deadlines, timeframes, and scheduling considerations.</p>
</li>
<li><p><strong><s>Civilian Considerations</s> Cost Considerations:</strong> Budget constraints and financial planning.</p>
</li>
</ul>
<h1 id="heading-breaking-down-mett-tc-for-devrel"><strong>Breaking Down METT-TC for DevRel</strong></h1>
<h2 id="heading-mission"><strong>Mission</strong></h2>
<p>The mission is the cornerstone of your DevRel strategy. Breaking it down into the “who, what, when, where, why” ensures that every aspect is clear and actionable.</p>
<h3 id="heading-who"><strong>Who</strong></h3>
<p>Every DevRel role is different. Some DevRels may be only focused on developers, while others may be part of every conversation. It's always important to clearly define WHO, as a DevRel, you need to focus on. Your time and attention is finite, so clearly stating who you're supposed to focus on is very important when setting expectations with your peers and superiors.</p>
<ul>
<li><p><strong>Developers:</strong> Determine the specific group of developers you aim to reach (e.g., Solidity developers, frontend developers, blockchain engineers).</p>
</li>
<li><p><strong>Partners:</strong> Identify potential collaborators like educational institutions, tech communities, or industry influencers.</p>
</li>
<li><p><strong>Internal Teams:</strong> Recognize internal stakeholders such as product managers, engineers, marketing, and sales teams who will support the mission.</p>
</li>
</ul>
<h3 id="heading-what"><strong>What</strong></h3>
<p>This is the most important component of the mission. Without understanding the targets of the mission, it becomes extremely easy to derail. This may seem a bit arbitrary, but it's extremely easy to get distracted with "side quests" that don't really assist in reaching the set targets. By clearly defining your targets, you ensure that every action taken—whether it’s producing educational content, hosting community events, or improving documentation—contributes meaningfully to your overarching mission. Establishing these targets not only will help guide your day-to-day efforts but also makes it easier to measure progress, celebrate successes, and refine your approach as needed.</p>
<ul>
<li><p><strong>Specific Goals</strong>: Outline clear targets such as increasing platform adoption by 30%, hosting quarterly webinars, or launching a new SDK.</p>
</li>
<li><p><strong>KPIs</strong>: Establish key performance indicators like active developer count, engagement rates, or number of contributions.</p>
</li>
</ul>
<h3 id="heading-when">When</h3>
<p>Imagine multiple special ops teams in a coordinated task force moving in all on the same target. In this high intensity scenario, timing is more than just completing the mission quickly. It’s about getting the job done with speed, diligence, and synchronicity.</p>
<p>Well, time management is just as crucial for executing your DevRel strategy effectively. You need to be strategic in how you schedule and sequence your initiatives. Your DevRel Strategy’s mission is rarely the only mission underway, and you must account for the other priorities your team members are juggling. For example, if you share a single graphic designer with another project, you’ll need to factor in their limited availability and adjust timelines accordingly.</p>
<p>Of course, team member availability isn’t the only consideration. The most strategic founders, CEOs, and Senior DevRels excel at running multiple missions in parallel, carefully coordinating their timelines and completion dates based on priorities and dependencies. There’s no point in moving ahead with your content strategy before your documentation is updated, for instance. <em>(We’ll delve into mission prioritization in a later article.)</em></p>
<ul>
<li><p><strong>Milestones</strong>: Set achievable deadlines for each objective, breaking larger goals into manageable steps.</p>
</li>
<li><p><strong>Schedules</strong>: Align initiatives with industry events or product release cycles to maximize impact and relevance.</p>
</li>
</ul>
<h3 id="heading-where">Where</h3>
<p>Determining where to focus your DevRel efforts is about more than just picking a location—it’s about understanding the environments in which your target developers operate. In any military operation, you want to know everything about the terrain you’re entering:</p>
<ul>
<li><p>Where are the vantage points?</p>
</li>
<li><p>Where are the exits?</p>
</li>
<li><p>How easy is it to get in?</p>
</li>
<li><p>How difficult is it to get out?</p>
</li>
<li><p>What type of resources should we consider based on the terrain?</p>
</li>
</ul>
<p>As for DevRel, you’ll want to consider similar questions:</p>
<ul>
<li><p>Are you aiming to reach communities in a specific region or language group?</p>
</li>
<li><p>Do you need to prioritize online gatherings over physical meetups, or vice versa?</p>
</li>
</ul>
<p>Recognizing the geographic and digital “terrain” you’re working in helps ensure you place your energy where it can have the greatest impact. By selecting the right areas—whether they be physical hubs of innovation or online platforms where your audience already congregates—you can more efficiently tailor your messaging, resources, and engagement strategies to match local norms and developer preferences.</p>
<ul>
<li><p><strong>Geographical Focus</strong>: Determine whether to target local, regional, or global developer communities based on factors like language, market maturity, and proximity to key partners.</p>
</li>
<li><p><strong>Platforms</strong>: Identify the online spaces where your target developers are most active—such as GitHub, Stack Overflow, or specialized forums—and engage with them directly in those environments.</p>
</li>
</ul>
<h3 id="heading-why">Why</h3>
<blockquote>
<p>“When you know why you’re doing what you’re doing, when you know what you’re trying to accomplish, when you know what the end state is supposed to be, that’s when you can open your mind to the different routes and the different paths that will get you there.”</p>
<p>— Jocko Willink</p>
</blockquote>
<p>Clarifying the underlying purpose behind your DevRel strategy ensures that you’re not just going through the motions, but actually driving meaningful impact. If the "what" and "when" define your targets and timelines, then the "why" provides the compass that keeps you headed in the right direction. By aligning your mission with the company’s larger goals and understanding the intrinsic motivations and challenges faced by the developer community, you ensure that every initiative you undertake resonates with both your organization and your audience.</p>
<ul>
<li><p><strong>Company Vision</strong>: Make sure your DevRel mission supports the overarching objectives of the company, reinforcing its values and long-term direction.</p>
</li>
<li><p><strong>Developer Needs</strong>: Identify the core motivations, pain points, and desires of your developers to ensure that the solutions you provide genuinely address their concerns and enhance their experience.</p>
</li>
</ul>
<h2 id="heading-ecosystem"><strong>Ecosystem</strong></h2>
<p>In the same way that special operations forces must know the lay of the land before engaging, a DevRel team needs a comprehensive understanding of its operational environment to succeed. Your platform doesn’t live in isolation—it's part of a dynamic ecosystem where trends shift, competitors move, and regulations evolve.</p>
<p>For a great deal of this year, everyone was getting excited about zero-knowledge and fully-homomorphic encryption while, at the moment I’m writing this, AI agents like Eliza are the newest craze. Understanding how the terrain can suddenly shift and being able to make critical decisions that allow you and your team to capture and maintain developers in your ecosystem is a critical skill for all DevRels.</p>
<p>You mus gain and maintain situational awareness in this space. This allows you to identify strategic opportunities, prepare for potential threats, and position your platform to thrive in the long run.</p>
<h3 id="heading-industry-trends"><strong>Industry Trends</strong></h3>
<p>Like studying weather patterns before a mission, keeping a close eye on industry trends helps you anticipate changes that could impact your platform. Are new protocols gaining traction? Is developer interest shifting from one technology stack to another? By tracking these movements, you’ll be better equipped to adjust your messaging, evolve your product offerings, and stay ahead of the curve.</p>
<ul>
<li><p><strong>Emerging Technologies</strong>: Keep tabs on evolving blockchain standards, smart contract innovations, and the broader Web3 landscape, ensuring you’re ready to adapt as the field matures.</p>
</li>
<li><p><strong>Market Demands</strong>: Monitor where developer attention is heading—are they rallying around decentralized finance, NFTs, or layer-2 scaling solutions? Use these insights to align your outreach and resources accordingly.</p>
</li>
</ul>
<h3 id="heading-competitor-analysis"><strong>Competitor Analysis</strong></h3>
<p>Just as a recon team studies their adversaries’ strengths and weaknesses, understanding your competitors gives you a tactical advantage. Identifying their capabilities, community engagement levels, and messaging strategies helps you carve out a unique position, refine your approach, and highlight what sets your platform apart. This goes right back to my absolute favorite quote that’s mentioned in the beginning of this article. You should never go into a fair fight. Through diligent research and recon, you should know more about the competition than they even know about themselves.</p>
<ul>
<li><p><strong>Strengths and Weaknesses</strong>: Assess where rival platforms excel—be it documentation quality, active community support, or cutting-edge tooling—and determine where they fall short.</p>
</li>
<li><p><strong>Market Positioning</strong>: Pinpoint your unique value proposition. Whether it’s unparalleled scalability, robust security, or superior developer experience, make sure it’s clear why builders should choose your platform.</p>
</li>
</ul>
<h3 id="heading-regulatory-landscape"><strong>Regulatory Landscape</strong></h3>
<p>In any high-stakes mission, ignoring the rules of engagement can be costly. The Web3 space is dotted with varying regulatory requirements that can impact your platform’s adoption, credibility, and long-term viability. Understanding these regulations in advance ensures you don’t get blindsided by compliance issues that could derail your progress.</p>
<ul>
<li><p><strong>Data Protection</strong>: If you work closely with your marketing team, then you should familiarize yourself with privacy laws like GDPR and CCPA. Ensuring compliance will not only keep you out of hot water but also build trust with developers and users.</p>
</li>
<li><p><strong>Blockchain Regulations</strong>: Stay informed about how different regions classify tokens, smart contracts, and decentralized applications. This foresight allows you to adjust your strategy, avoid costly missteps, and maintain operational freedom.</p>
</li>
</ul>
<h3 id="heading-community-sentiment"><strong>Community Sentiment</strong></h3>
<p>In the field, morale can determine whether a mission succeeds or fails. Similarly, the sentiment of your developer community can signal the health of your ecosystem. In special operations, officers don’t simply hang back and command their teams from the rear. They move with their team as equals. As a DevRel, you should be active within your community. Without being active in your community, you’ll likely not have a feel for your community’s tone and engagement levels. If the tone is positive and engagement is high, you’re on the right path. If not, you may need to pivot your messaging, resources, or support strategies. Regularly taking the community’s pulse ensures you remain responsive, agile, and ready to address concerns before they escalate. But, that simply can’t be done without being an active participant within your own community.</p>
<ul>
<li><p><strong>Feedback Collection</strong>: Listen actively and participate to forums, Discord channels, and social media conversations. Understanding what developers value—or what they struggle with—empowers you to respond with meaningful solutions.</p>
</li>
<li><p><strong>Engagement Levels</strong>: Measure how frequently developers participate, contribute code, or attend community events. High engagement often indicates a thriving ecosystem, while dwindling involvement may signal areas that need attention.</p>
</li>
</ul>
<p>By viewing your ecosystem through the lens of a special operations mission, you recognize the importance of continuous reconnaissance, strategic positioning, and proactive adjustments when the terrain changes. Armed with insights into industry trends, competitor landscapes, regulations, and community sentiment, you’ll be prepared to guide your DevRel strategy toward success—no matter what the terrain ahead may hold.</p>
<h2 id="heading-target-market"><strong>Target Market</strong></h2>
<p>In the army rangers, intelligence gathering isn’t limited to the lay of the land—it demands a deep understanding of the local population. Knowing where enemy combatants might hide or where friendly forces find sanctuary is vital, but so is grasping the subtler dynamics at play. Are there sympathetic people who can share critical intel? Are there local leaders whose influence could sway outcomes in your favor? Understanding the human element allows operators to move with confidence and avoid missteps that could jeopardize the entire mission.</p>
<p>Your DevRel initiatives are not so different. Just as special operators must be intimately familiar with the cultural, social, and psychological nuances of the people in their operational area, you must develop an equally comprehensive understanding of your target developers. Who do they trust and turn to for advice? What drives them to adopt a new technology, and what frustrates them enough to walk away? Where do they gather to discuss issues, share knowledge, and form their professional identities?</p>
<p>These insights aren’t just “nice to have”—they’re the difference between a DevRel strategy that falls flat and one that resonates so deeply that developers feel you’ve anticipated their every need. By knowing your target market at this granular level, you become more than just another platform vying for attention; you become a trusted ally, a guide who understands the challenges on their path and helps them overcome obstacles.</p>
<p>In other words, if you approach your target market with the same rigor and thoroughness that army rangers apply to human intelligence gathering, you’ll not only improve the precision of your outreach but also build lasting connections founded on trust, relevance, and tangible value.</p>
<h3 id="heading-demographics"><strong>Demographics</strong></h3>
<p>Just as a recon unit profiles the local population to determine potential allies, neutral parties, or those sympathetic to opposing forces, you need to identify the fundamental attributes of your target developers. Are they seasoned blockchain veterans who appreciate deep technical dives, or newcomers looking for clear, beginner-friendly guidance? Understanding where your developers stand in terms of skill level, geographic distribution, and industry focus provides the groundwork for everything else.</p>
<ul>
<li><p><strong>Experience Level</strong>: Recognize whether you’re targeting novices just starting their web3 journey or expert developers seeking cutting-edge solutions, and tailor your materials accordingly. Maybe you’ll intend to target both! Just be considerate of time and cost commitments to target two different target markets.</p>
</li>
<li><p><strong>Geographic Location</strong>: Prioritize regions known for vibrant developer communities or particular industry clusters. Keep in mind language barriers, cultural nuances, and time zones when planning outreach and support.</p>
</li>
<li><p><strong>Industry Focus</strong>: Consider which sectors—finance, gaming, supply chain, and beyond—align with your platform’s strengths. Positioning yourself where your capabilities can shine makes your efforts far more impactful.</p>
</li>
</ul>
<h3 id="heading-psychographics"><strong>Psychographics</strong></h3>
<p>Demographics tell you who these developers are, but psychographics explain why they do what they do. This is where you uncover the heart of their decision-making process. Are they attracted by pioneering technology, or do they seek stability and dependable documentation? Do they crave community recognition, or do they prefer to quietly build and experiment? Understanding what drives them ensures that you’re not just handing out tools, but providing genuine value that resonates on a personal level.</p>
<ul>
<li><p><strong>Motivations</strong>: Identify what lights a fire in them—top-notch performance, world-class support, a supportive community, or a clear roadmap.</p>
</li>
<li><p><strong>Pain Points</strong>: Pinpoint their frustrations. Maybe they’ve struggled with poor tooling elsewhere, encountered opaque documentation, or lacked responsive technical support. Addressing these issues head-on earns trust and loyalty.</p>
</li>
<li><p><strong>Preferences</strong>: From learning styles (video tutorials, documentation, live workshops) to feedback mechanisms (Discord chats, GitHub Issues, Twitter threads), find out how they prefer to learn and communicate, then meet them there.</p>
</li>
</ul>
<h3 id="heading-behavioral-patterns"><strong>Behavioral Patterns</strong></h3>
<p>In the field, anticipating how people will react is crucial. For DevRel, understanding your audience’s behavior—how often they engage, how quickly they adopt new technologies, and what kind of content they gravitate toward—lets you shape your initiatives for maximum effect. If your target developers love hands-on challenges, spin up regular hackathons. If they respond best to incremental updates, break your releases into digestible parts.</p>
<ul>
<li><p><strong>Learning Methods</strong>: Identify whether developers value structured courses, prefer quickstart guides, or enjoy self-directed exploration.</p>
</li>
<li><p><strong>Engagement Habits</strong>: Determine when they’re most active online, how frequently they participate in community forums, and which events or content formats command their attention.</p>
</li>
<li><p><strong>Technology Adoption</strong>: Assess whether they’re early adopters eager to test new features or cautious integrators who wait for proven stability. This insight helps you time releases and announcements for the greatest impact.</p>
</li>
</ul>
<h3 id="heading-channels"><strong>Channels</strong></h3>
<p>Even the most finely tuned message won’t resonate if it’s not delivered in the right place. Army rangers how important it is to know to pick the right insertion point; similarly, you must choose the right channels to connect with your target developers. Whether it’s through specialized forums, social media platforms, or chat communities, meeting them where they already spend their time fosters trust and encourages ongoing engagement.</p>
<ul>
<li><p><strong>Social Media</strong>: Figure out if your audience is more active on Twitter, LinkedIn, YouTube, or emerging platforms. Tailor your messaging style to fit these environments.</p>
</li>
<li><p><strong>Community Hubs</strong>: Identify where developers naturally gather—GitHub for code, Discord for real-time conversations, Slack for professional communities—and establish a meaningful presence there.</p>
</li>
</ul>
<p>By applying the same meticulous care that special operations teams use when studying local populations, you can understand who your developers are at their core. Armed with insights into their demographics, psychographics, behaviors, and preferred channels, you’ll craft a DevRel strategy that doesn’t just speak to your audience, but genuinely resonates. In doing so, you elevate your platform from yet another player in the ecosystem to a trusted partner guiding developers toward success.</p>
<h2 id="heading-troops-and-support-available"><strong>Troops and Support Available</strong></h2>
<p>No matter how well-defined your mission is, it’s only as strong as the team and resources you have at your disposal. In the world of DevRel, “troops” can mean anyone from Developer Advocates to Marketing Specialists, and “support” can refer to the tools, platforms, and partners that help bring your strategy to life. Understanding who’s on your bench and what resources you can tap into ensures that every initiative, event, and piece of content can be executed at a high standard. More importantly, it helps you allocate your energy and bandwidth where they’ll make the biggest impact.</p>
<h3 id="heading-internal-teams"><strong>Internal Teams</strong></h3>
<p>Your internal team is the backbone of your DevRel strategy. By clearly defining roles and responsibilities, you can play to each individual’s strengths and create a streamlined, cohesive operation.</p>
<ul>
<li><p><strong>Developer Advocates</strong>: Craft educational content, engage directly with developers, and serve as the frontline for support and guidance.</p>
</li>
<li><p><strong>Technical Writers</strong>: Produce detailed, accessible documentation, how-to guides, and tutorials that lower the barrier to entry for new developers.</p>
</li>
<li><p><strong>Community Managers</strong>: Foster vibrant online forums, coordinate events, and ensure that everyone feels welcome and heard.</p>
</li>
</ul>
<p><strong>Engineering Support</strong>: Your core developers and QA engineers bring deep technical knowledge, helping you address complex developer issues and maintain a high standard of product quality. They’re your go-to for ensuring that tools, APIs, and documentation are reliable and user-friendly.</p>
<p><strong>Marketing and Sales Teams</strong>: Marketing Specialists amplify your message through campaigns, social media, and branding, while Sales Engineers bridge the gap between technical capabilities and customer needs. Together, they help you position your platform in the marketplace and communicate its value effectively.</p>
<h3 id="heading-prioritizing-resources"><strong>Prioritizing Resources</strong></h3>
<p>Just as a well-led squad knows how to distribute responsibilities, a successful DevRel team understands how to manage resources strategically. Not every initiative warrants the same level of attention, and not every team member needs to be involved at every stage.</p>
<ul>
<li><p><strong>Assessing Strengths</strong>: Match tasks to the skill sets of your team, ensuring that no one is overburdened and that quality doesn’t suffer.</p>
</li>
<li><p><strong>Strategic Deployment</strong>: Reserve the bulk of your resources—whether that’s time, talent, or tools—for the initiatives that have the greatest potential to move the needle. Keep a steady baseline of support for essential, ongoing efforts.</p>
</li>
</ul>
<h3 id="heading-tools-and-platforms"><strong>Tools and Platforms</strong></h3>
<p>Your internal “troops” need the right gear to excel. Equipping them with effective tools and platforms not only streamlines their workflow but also enhances the developer experience.</p>
<ul>
<li><p><strong>Documentation Platforms</strong>: Host and manage clear, searchable documentation using platforms like GitBook or Read the Docs.</p>
</li>
<li><p><strong>Code Repositories</strong>: Leverage GitHub or GitLab for open-source collaboration, code reviews, and issue tracking.</p>
</li>
<li><p><strong>Community Forums</strong>: Implement solutions like Discourse or Vanilla Forums to build an accessible hub for Q&amp;A, discussions, and announcements.</p>
</li>
<li><p><strong>Analytics and Monitoring</strong>: Use Orbit for community analytics and New Relic or Datadog for performance monitoring to keep your finger on the ecosystem’s pulse and quickly address issues.</p>
</li>
</ul>
<h3 id="heading-external-partners"><strong>External Partners</strong></h3>
<p>Beyond your internal team, external allies can expand your reach, enhance your credibility, and fill gaps in your expertise. Selecting the right collaborators—be they influencers, educational institutions, or industry organizations—can magnify your impact far beyond what you could achieve alone.</p>
<ul>
<li><p><strong>Influencers and Thought Leaders</strong>: Partner with well-respected figures in the blockchain or developer community to co-create content, host webinars, or kick off new initiatives. Their endorsement and audience can lend instant credibility and broaden your visibility.</p>
</li>
<li><p><strong>Educational Institutions</strong>: Forge relationships with universities or coding bootcamps to offer courses, workshops, and research collaborations. These partnerships help cultivate the next generation of developers who are already familiar with your platform.</p>
</li>
<li><p><strong>Industry Organizations</strong>: Join standards committees or contribute to open-source projects that are relevant to your ecosystem. This helps position your platform as a responsible, forward-thinking participant in the broader tech community.</p>
</li>
</ul>
<h3 id="heading-prioritizing-external-support"><strong>Prioritizing External Support</strong></h3>
<p>Just as you strategically deploy internal resources, you should also carefully select which external partnerships deserve the most attention and investment.</p>
<ul>
<li><p><strong>Mission Fit</strong>: Choose partners whose goals, audience, and values align well with yours.</p>
</li>
<li><p><strong>Impact Potential</strong>: Look for relationships that offer substantial audience reach or long-term value, ensuring that your collaborations aren’t just one-off events but stepping stones toward broader strategic objectives.</p>
</li>
</ul>
<p>In essence, knowing who’s on your team, what tools you have, and which partnerships to pursue allows you to approach DevRel with confidence and precision. By thoughtfully managing these “troops and supports,” you ensure that every effort—no matter how big or small—has the backing it needs to succeed.</p>
<h2 id="heading-time-available"><strong>Time Available</strong></h2>
<p>In DevRel, timing is everything. Even the best-planned initiatives can fall flat if they’re launched too late or rushed out too early. Effective time management goes beyond setting arbitrary deadlines—it involves carefully orchestrating your projects, aligning with external events, and taking into account the human aspect of your team’s availability. By thoughtfully planning when to execute each part of your strategy, you increase your odds of hitting the mark and making a lasting impact.</p>
<h3 id="heading-project-timelines"><strong>Project Timelines</strong></h3>
<p>Your DevRel strategy should unfold over carefully planned intervals. Setting realistic deadlines helps keep everyone focused and moving in the same direction, while also providing checkpoints to measure progress and make adjustments along the way.</p>
<ul>
<li><p><strong>Short-Term Goals</strong>: Identify what needs to be accomplished in the next few months, ensuring that immediate priorities don’t get overshadowed by long-term plans.</p>
</li>
<li><p><strong>Long-Term Objectives</strong>: Establish annual milestones that align with the company’s broader timeline, ensuring that your DevRel initiatives contribute to overarching business goals.</p>
</li>
</ul>
<h3 id="heading-event-schedules"><strong>Event Schedules</strong></h3>
<p>Events—whether online webinars, hackathons, or industry conferences—offer prime opportunities to showcase your platform and connect with developers directly. By syncing your roadmap with these events, you can debut new features, announce partnerships, or kick off community programs at moments when your audience is already paying close attention.</p>
<ul>
<li><p><strong>Industry Conferences</strong>: Launch new products or share major updates at significant industry gatherings, capturing the interest of a concentrated audience.</p>
</li>
<li><p><strong>Seasonal Trends</strong>: Pay attention to patterns in developer engagement (e.g., increased activity during hackathon seasons) and adjust your calendar accordingly.</p>
</li>
</ul>
<h3 id="heading-resource-availability"><strong>Resource Availability</strong></h3>
<p>Your team’s time and energy are finite resources. Ensuring that the right people are available at the right moments is critical to keeping projects on track. Recognize when team members are at their best, balance workloads to prevent burnout, and plan around known absences or reduced productivity periods.</p>
<ul>
<li><p><strong>Team Schedules</strong>: Confirm availability well in advance of deadlines and events, and adjust timelines if key contributors are on leave.</p>
</li>
<li><p><strong>Workload Balancing</strong>: Spread responsibilities across the team so that no single individual becomes a bottleneck. This helps maintain steady progress and consistent quality.</p>
</li>
</ul>
<h3 id="heading-market-windows"><strong>Market Windows</strong></h3>
<p>Just as a good sailor waits for favorable winds, a savvy DevRel professional chooses the best windows to launch new initiatives. Aligning your product releases, updates, and campaigns with moments when the market is most receptive can dramatically improve your results.</p>
<ul>
<li><p><strong>Product Readiness</strong>: Don’t rush to market with half-baked features. Wait until you’re confident in their stability and value, ensuring a smooth developer experience.</p>
</li>
<li><p><strong>Competitive Landscape</strong>: Keep tabs on what your competitors are doing. Launching a major update right after a competitor steals the spotlight might cause you to lose momentum—or, conversely, timing your announcement to outshine a competitor’s release can give you an edge.</p>
</li>
</ul>
<p>By taking a strategic approach to timing, you’re not just meeting deadlines—you’re setting yourself up to meet developers at the perfect moment, fully prepared, and with the resources in place to deliver a seamless experience.</p>
<h2 id="heading-cost-considerations"><strong>Cost Considerations</strong></h2>
<p>Even the most ambitious DevRel strategies must work within financial boundaries. Understanding and managing your costs can mean the difference between a thriving, sustainable program and one that quickly runs out of steam. By carefully assessing your budget, calculating potential ROI, and planning for the unexpected, you ensure that every dollar spent contributes directly to your overarching goals. Smart financial stewardship also gives you the flexibility to pivot when market conditions change or new opportunities arise.</p>
<h3 id="heading-budget-constraints"><strong>Budget Constraints</strong></h3>
<p>A clear grasp of your financial situation is the baseline for all DevRel decision-making. Without knowing exactly how much you have to spend—and how that budget breaks down per initiative—you risk overcommitting resources or missing out on high-impact opportunities.</p>
<ul>
<li><p><strong>Annual Budget</strong>: Start with a top-level view of your yearly DevRel allowance, factoring in known recurring costs and anticipated new endeavors.</p>
</li>
<li><p><strong>Per-Initiative Budgeting</strong>: Break down the big picture into smaller, manageable chunks. Assign budgets to specific events, content projects, and community programs, ensuring that each initiative has enough fuel to deliver meaningful results.</p>
</li>
</ul>
<h3 id="heading-cost-benefit-analysis"><strong>Cost-Benefit Analysis</strong></h3>
<p>Not all DevRel activities are created equal, and some may offer a better return on investment than others. Evaluating the potential impact of each effort against its price tag helps you make more informed decisions about where to allocate your resources.</p>
<ul>
<li><p><strong>Direct Costs vs. Benefits</strong>: Compare immediate outlays—like production costs for a webinar series—against direct payoffs, such as new developer sign-ups or increased community engagement.</p>
</li>
<li><p><strong>Long-Term Gains</strong>: Consider not only the immediate results but also the longer-term value of initiatives that build trust, grow brand recognition, or foster a vibrant, sustainable developer ecosystem.</p>
</li>
</ul>
<h3 id="heading-risk-assessment"><strong>Risk Assessment</strong></h3>
<p>No strategy comes without risk, and managing your finances in DevRel requires a forward-looking perspective. By identifying where costs might balloon unexpectedly or where certain bets may fail to pay off, you can put safeguards in place that minimize financial pain and protect the health of your program.</p>
<ul>
<li><p><strong>Overruns</strong>: Forecast which initiatives might exceed their initial budget and plan contingencies in advance.</p>
</li>
<li><p><strong>Underperformance</strong>: Acknowledge that not every idea will hit its target ROI and be ready to cut losses early if something isn’t working.</p>
</li>
</ul>
<h3 id="heading-funding-opportunities"><strong>Funding Opportunities</strong></h3>
<p>When your budget is constrained, creativity and collaboration become your best friends. Seeking external funding sources or forming partnerships can help stretch your resources further, allowing you to tackle larger or more complex initiatives without bearing the full financial burden.</p>
<ul>
<li><p><strong>Grants and Sponsorships</strong>: Look for grants from nonprofit organizations or consider sponsorship deals with companies that share a similar target audience.</p>
</li>
<li><p><strong>Cost-Sharing Partnerships</strong>: Collaborate with complementary brands or organizations to split expenses and broaden your collective reach.</p>
</li>
</ul>
<p>By keeping a close eye on costs from the start, you not only ensure that you’re making the most of your available resources but also position your DevRel efforts for long-term sustainability and success. A well-managed budget provides the stability you need to adapt, grow, and continue delivering value to your developer community.</p>
<hr />
<h1 id="heading-applying-mett-tc-to-devrel-strategies"><strong>Applying METT-TC to DevRel Strategies</strong></h1>
<h2 id="heading-crafting-the-mission-statement">Crafting the Mission Statement</h2>
<blockquote>
<p>"The more clearly we understand what we're trying to accomplish, the greater effect we can have in accomplishing it. People need to understand the purpose behind what they're being asked to do." — General James Mattis, USMC</p>
</blockquote>
<p>In Army Ranger operations, a mission statement isn't just a formality—it's the foundation upon which everything else is built. Before a single boot hits the ground, every Ranger must understand not just what they're doing, but why it matters and how success will be measured. A mission statement that lacks clarity isn't just unhelpful; it's dangerous.</p>
<p>The same holds true in Developer Relations. Your mission statement serves as the North Star that guides every decision, initiative, and resource allocation. It needs to be precise, actionable, and firmly aligned with your company's strategic objectives. A vague mission like "improve developer experience" leaves too much room for interpretation and makes it impossible to measure success.</p>
<p>Instead, craft a mission statement that addresses the core components we've discussed: the who, what, when, where, and why of your DevRel strategy. Make it specific enough to provide clear direction but flexible enough to adapt to changing conditions—just as Rangers must sometimes adjust their tactics while keeping the overall mission objective in focus.</p>
<h3 id="heading-mission-statement-example">Mission Statement Example</h3>
<p><strong>Mission:</strong> To establish Arweave as the preferred permanent storage solution for Web3 builders by demonstrating its unique data persistency advantages, developing comprehensive technical resources, and nurturing an active builder ecosystem, with the goal of increasing protocol usage by 75% within the next year.</p>
<p>This statement has it all:</p>
<ul>
<li><p><strong>Who:</strong> Web3 builders</p>
</li>
<li><p><strong>What:</strong> Establish Arweave as preferred storage solution, demonstrate advantages, develop resources, nurture ecosystem</p>
</li>
<li><p><strong>When:</strong> Within the next year</p>
</li>
<li><p><strong>Where:</strong> Within the Web3 permanent storage context</p>
</li>
<li><p><strong>Why:</strong> To increase protocol usage by 75%</p>
</li>
</ul>
<p>By establishing this level of clarity from the outset, you ensure that every team member understands exactly what they're working toward. Just as a Ranger team visualizes mission success before execution, your DevRel team should be able to envision what accomplishing this mission looks like.</p>
<p>Remember, in both special operations and DevRel, mission creep is a constant danger. A well-crafted mission statement serves as a guardrail, helping you recognize when you're veering off course and allowing you to realign your efforts before resources are wasted. When someone proposes a new initiative, you can simply ask: "How does this help us establish Arweave as the preferred permanent storage solution and increase protocol usage?" If there's no clear answer, it's probably not worth pursuing.</p>
<p>The mission statement isn't just for your internal team—it's something you should be able to communicate to stakeholders throughout your organization. When leadership understands exactly what you're trying to accomplish and how it ties to broader company goals, you're more likely to secure the support and resources you need to succeed.</p>
<p>In short: Treat your mission statement with the same seriousness that Rangers treat their mission briefings. Get it right from the start, refer to it often, and use it as the benchmark against which all activities are measured. In the unpredictable terrain of Developer Relations, a clear mission won't just guide your way—it will be the difference between scattered efforts and strategic impact.</p>
<h2 id="heading-analyzing-the-ecosystem">Analyzing the Ecosystem</h2>
<blockquote>
<p>"If I had six hours to chop down a tree, I'd spend the first four sharpening the axe." — Abraham Lincoln (Often cited in military strategic planning)</p>
</blockquote>
<p>While Lincoln wasn't a military commander, his wisdom about preparation resonates deeply with military planning principles. In both special operations and DevRel, proper analysis before action isn't optional—it's essential for mission success.</p>
<p>Think of your ecosystem analysis as the intelligence gathering phase before a high-stakes mission. Rangers don't just charge into hostile territory without first understanding the landscape, identifying potential adversaries, and assessing environmental factors that could impact their operation. Similarly, your DevRel strategy requires a meticulous assessment of the technological battlefield where you'll be operating.</p>
<h3 id="heading-swot-analysis-your-strategic-intelligence-brief">SWOT Analysis: Your Strategic Intelligence Brief</h3>
<p>Start with a SWOT analysis—a tactical assessment that would make any mission planner proud. This isn't just a corporate exercise; it's your battle plan foundation:</p>
<ul>
<li><p><strong>Strengths:</strong> What unique capabilities does your platform offer that competitors can't match? Perhaps Arweave's permanent storage guarantees or cost-effective data persistence give you a tactical advantage in specific scenarios.</p>
</li>
<li><p><strong>Weaknesses:</strong> Where are you vulnerable? Be brutally honest. Maybe your documentation isn't comprehensive enough for enterprise developers, or your onboarding process has too much friction. Identifying these weaknesses isn't admitting defeat—it's preventing ambush.</p>
</li>
<li><p><strong>Opportunities:</strong> Scout for openings in the market where your platform can establish a stronghold. Is there an emerging sector (like NFT metadata storage or decentralized social media) where permanent storage is becoming critical but current solutions are inadequate?</p>
</li>
<li><p><strong>Threats:</strong> What forces could undermine your mission? New competing protocols, changing regulatory landscapes, or shifts in developer preferences can all pose serious threats to your objectives.</p>
</li>
</ul>
<p>Remember, a surface-level SWOT analysis is like reconnaissance that stops at the perimeter—dangerously incomplete. Dig deep, gather input from multiple sources, and challenge your assumptions. The most dangerous threats are often the ones you've failed to identify.</p>
<h3 id="heading-market-research-continuous-intelligence-gathering">Market Research: Continuous Intelligence Gathering</h3>
<p>In special operations, intelligence isn't a one-time collection—it's continuous. Similarly, your market research should be an ongoing operation:</p>
<ul>
<li><p>Deploy regular developer surveys to gauge sentiment, needs, and pain points.</p>
</li>
<li><p>Monitor GitHub repositories, Stack Overflow questions, and Discord channels to identify emerging trends.</p>
</li>
<li><p>Infiltrate developer conferences (even those not directly related to your technology) to understand how the broader ecosystem is evolving.</p>
</li>
<li><p>Track competitor announcements and community reactions with the same attention you'd give to enemy movements.</p>
</li>
</ul>
<p>Your goal is to build an accurate, real-time map of the developer landscape. This intelligence will help you anticipate shifts, identify opportunities before competitors do, and position your resources where they'll have maximum impact.</p>
<h3 id="heading-technology-stack-evaluation-assessing-terrain-and-resources">Technology Stack Evaluation: Assessing Terrain and Resources</h3>
<p>Just as Rangers must understand how their equipment will perform in different environments, you need to thoroughly evaluate how your technology integrates with the tools developers already use:</p>
<ul>
<li><p>Catalog the most common frameworks, languages, and platforms in your target developer segments.</p>
</li>
<li><p>Test integration points meticulously, identifying friction that could slow developer adoption.</p>
</li>
<li><p>Consider compatibility not just with current technology stacks but with emerging tools gaining traction.</p>
</li>
<li><p>Develop clear pathways that show developers how to incorporate your technology into their existing workflows.</p>
</li>
</ul>
<p>This evaluation isn't a theoretical exercise—it's practical intelligence that directly impacts your tactical approach. If you discover that many developers in your target market use a particular CI/CD pipeline, developing seamless integration with that pipeline becomes a priority mission objective.</p>
<hr />
<p>The ecosystem analysis phase may not have the glamour of community launches or developer events, but it's where battles are won or lost before they're even fought. As Rangers know, superior intelligence doesn't just give you an edge—it fundamentally changes the nature of the engagement.</p>
<p>When you thoroughly understand your ecosystem, you're no longer reacting to market conditions—you're anticipating them. You're not competing on even ground—you're selecting the terrain that gives you the advantage. And most importantly, you're not fighting fair—you're engaging precisely where and when your platform's strengths will be most decisive.</p>
<h2 id="heading-assessing-troops-and-support">Assessing Troops and Support</h2>
<blockquote>
<p>"The strength of the team is each individual member. The strength of each member is the team." — General Phil Schuyler (adapted from military leadership principles)</p>
</blockquote>
<p>In any military operation, the success of the mission hinges not just on the plan, but on the capabilities of the troops executing it and the support systems backing them. A strategy that doesn't account for the strengths and limitations of your available forces isn't a strategy—it's a fantasy.</p>
<p>When Rangers prepare for deployment, they don't just inventory weapons and equipment—they assess the specialized skills within the unit, identify capability gaps, and ensure each team member is positioned where their skills will have maximum impact. Your DevRel operation demands the same rigorous personnel assessment.</p>
<h3 id="heading-team-skills-strategic-positioning-of-forces">Team Skills: Strategic Positioning of Forces</h3>
<p>Start by conducting an honest inventory of your team's capabilities, just as a commander would evaluate their unit before a mission:</p>
<ul>
<li><p><strong>Skill Mapping:</strong> Document the technical expertise, communication abilities, and domain knowledge of each team member. Which languages do they code in? Which frameworks are they comfortable with? How experienced are they with your platform?</p>
</li>
<li><p><strong>Strength Alignment:</strong> Position team members where their natural talents can shine. Your technically brilliant engineer who struggles with public speaking might create exceptional developer tools rather than presenting at conferences. Your charismatic community builder with moderate technical skills might lead engagement initiatives rather than writing complex documentation.</p>
</li>
<li><p><strong>Cross-Functional Capabilities:</strong> Identify team members who can operate across multiple domains—these are your special forces operators who can adapt to changing conditions and fill critical gaps when needed.</p>
</li>
</ul>
<p>Remember, putting people in positions that leverage their strengths isn't just good for morale—it's a force multiplier. As Rangers know, a small team operating in their element can outperform a much larger force that's misaligned.</p>
<h3 id="heading-resource-gaps-identifying-vulnerabilities">Resource Gaps: Identifying Vulnerabilities</h3>
<p>No unit goes into battle without understanding what resources they lack. Your DevRel operation requires the same clear-eyed assessment:</p>
<ul>
<li><p><strong>Missing Specializations:</strong> Do you need a dedicated technical writer? A video content creator? A data analyst to measure community engagement? Identify these gaps before they become mission-critical failures.</p>
</li>
<li><p><strong>Tool Deficiencies:</strong> Evaluate whether you have the right tools for content creation, community management, developer support, and metrics tracking. Using inadequate tools is like sending troops into battle with outdated equipment.</p>
</li>
<li><p><strong>Support Infrastructure:</strong> Assess whether you have sufficient backing from engineering, marketing, and product teams. Even the best DevRel team can't succeed without cross-functional support.</p>
</li>
</ul>
<p>Don't view resource gaps as failures—view them as intelligence that informs your strategy. If you know you're short on video production capabilities, you might emphasize written content in the short term while developing a plan to address the video gap.</p>
<h3 id="heading-training-needs-force-development">Training Needs: Force Development</h3>
<p>In special operations, training never stops. Even elite soldiers continuously hone existing skills and develop new ones. Your DevRel team should operate with the same commitment to growth:</p>
<ul>
<li><p><strong>Skill Development Plans:</strong> Create personalized training roadmaps for each team member, identifying both immediate needs and long-term growth opportunities.</p>
</li>
<li><p><strong>Cross-Training Initiatives:</strong> Build redundancy by ensuring multiple team members can handle critical functions. In military terms, this prevents a single point of failure from compromising the entire mission.</p>
</li>
<li><p><strong>Continuous Learning Culture:</strong> Foster an environment where learning is valued and rewarded. Encourage team members to share knowledge through internal workshops, documentation, and mentoring.</p>
</li>
</ul>
<p>The investment in training may seem costly in the short term, but it pays massive dividends in operational capability. A well-trained DevRel team can adapt to changing market conditions, respond to unexpected challenges, and capitalize on emerging opportunities with the same agility that makes special forces so effective.</p>
<hr />
<p>The most brilliant DevRel strategy will fail without the right team executing it. By methodically assessing your human resources, identifying gaps, and investing in continuous improvement, you're not just building a DevRel team—you're developing a specialized unit capable of achieving objectives that would be impossible for less disciplined organizations.</p>
<p>Remember that in both military operations and DevRel, it's not always the largest force that prevails—it's the one that understands its capabilities, addresses its weaknesses, and deploys its resources with strategic precision. Your careful assessment of troops and support isn't just an administrative exercise—it's the foundation of operational success.</p>
<h2 id="heading-managing-time-effectively">Managing Time Effectively</h2>
<blockquote>
<p>"Time is the scarcest resource and unless it is managed nothing else can be managed." — Lieutenant General Russel Honoré</p>
</blockquote>
<p>In both special operations and DevRel, time isn't just another resource—it's the one resource you can never reclaim once spent. When Rangers plan a mission, they don't just consider what needs to be done; they meticulously map out when each action must occur, accounting for dependencies, contingencies, and the inevitable friction that emerges when plans meet reality.</p>
<p>Your DevRel timeline demands the same level of strategic thought. A plan without realistic timeframes isn't a plan at all—it's a wish list. And wishes don't ship products, build communities, or drive adoption.</p>
<h3 id="heading-gantt-charts-mapping-the-operation">Gantt Charts: Mapping the Operation</h3>
<p>Just as military planners use operational timelines to visualize mission phases, your DevRel strategy benefits from visual planning tools:</p>
<ul>
<li><p><strong>Dependency Mapping:</strong> Use Gantt charts to clearly identify which activities depend on others. You can't launch a developer competition if the SDK it's based on isn't ready yet.</p>
</li>
<li><p><strong>Critical Path Identification:</strong> Highlight the sequence of tasks that determine your minimum timeline. These are the non-negotiable elements that require your most careful attention and resource allocation.</p>
</li>
<li><p><strong>Milestone Visualization:</strong> Mark key achievements and check-in points. These serve as rally points where you can assess progress and adjust course if necessary.</p>
</li>
</ul>
<p>While Gantt charts might seem like corporate tools far removed from battlefield planning, they serve the same function as a mission timeline—ensuring that everyone understands not just their individual responsibilities, but how their actions fit into the larger operation and impact the overall timeline.</p>
<h3 id="heading-agile-methodologies-tactical-flexibility">Agile Methodologies: Tactical Flexibility</h3>
<p>Rangers don't execute rigid plans that can't adapt to changing conditions—they maintain situational awareness and adjust tactics while keeping the strategic objective in focus. Agile methodologies bring this same adaptability to your DevRel operations:</p>
<ul>
<li><p><strong>Sprint Planning:</strong> Break longer initiatives into 2-4 week sprints with clear deliverables. This creates natural checkpoints for evaluation and course correction.</p>
</li>
<li><p><strong>Daily Stand-ups:</strong> Implement brief daily meetings where team members share progress, obstacles, and plans. This maintains operational awareness across the team and allows for rapid problem-solving.</p>
</li>
<li><p><strong>Retrospectives:</strong> After completing significant initiatives, conduct thorough reviews identifying what worked, what didn't, and how processes can improve for the next operation.</p>
</li>
</ul>
<p>Agile isn't just a software development methodology—it's a mindset that aligns perfectly with the adaptability required in both special operations and effective DevRel. It acknowledges that no plan survives first contact with the enemy (or in our case, the market), and builds in the mechanisms to adapt without losing sight of the mission.</p>
<h3 id="heading-time-buffers-planning-for-friction">Time Buffers: Planning for Friction</h3>
<p>In military operations, the concept of friction describes how seemingly simple tasks become complicated in the field. The same principle applies to DevRel initiatives:</p>
<ul>
<li><p><strong>Schedule Padding:</strong> Add 20-30% additional time to estimates for complex tasks, especially those involving multiple team members or dependencies.</p>
</li>
<li><p><strong>Contingency Planning:</strong> Identify critical path activities and develop specific backup plans for if (or when) they run behind schedule.</p>
</li>
<li><p><strong>Resource Reserves:</strong> Maintain a small reserve of uncommitted resources (both human and financial) that can be deployed to address unexpected challenges or opportunities.</p>
</li>
</ul>
<p>These buffers aren't admissions of poor planning—they're acknowledgments of reality. Just as Rangers carry additional ammunition, alternative communication channels, and emergency medical supplies, your DevRel operations need built-in safeguards against the inevitable complications that arise.</p>
<hr />
<p>Time management in DevRel isn't about cramming more activities into your calendar—it's about strategic allocation of a finite resource toward your highest-value objectives. By visualizing your timeline, embracing adaptability, and planning for inevitable complications, you ensure that your team's energy is directed toward meaningful progress rather than reactive firefighting.</p>
<p>Remember that in both military operations and DevRel, timing isn't just about efficiency—it's often the difference between success and failure. The team that can execute high-quality initiatives consistently, on a predictable timeline, will outperform competitors with more resources but less discipline in managing time.</p>
<h2 id="heading-evaluating-cost-considerations">Evaluating Cost Considerations</h2>
<blockquote>
<p>"Amateurs talk about tactics, but professionals study logistics." — General Omar Bradley</p>
</blockquote>
<p>In military operations, logistics—the art of moving and supplying troops—often determines who prevails. A force with superior weapons but inadequate supplies will ultimately fail against a well-provisioned opponent. Similarly, your DevRel strategy requires meticulous financial planning to sustain operations over time.</p>
<p>When Rangers prepare for deployment, they don't just focus on weapons and tactics—they ensure every aspect of the mission is properly resourced. The same discipline must be applied to your DevRel budget. Without proper financial planning, even the most brilliant strategy will collapse when resources run dry.</p>
<h3 id="heading-fixed-costs-your-operational-baseline">Fixed Costs: Your Operational Baseline</h3>
<p>Think of fixed costs as your base of operations—the foundational expenses that maintain your DevRel presence regardless of specific initiatives:</p>
<ul>
<li><p><strong>Personnel Expenses:</strong> Beyond just salaries, account for benefits, equipment, software licenses, and professional development for each team member. A Developer Advocate isn't just a line item—they're an investment in relationship-building and technical expertise.</p>
</li>
<li><p><strong>Infrastructure Costs:</strong> Developer portals, documentation platforms, community forums, and analytics tools all require ongoing funding. These are your command and control systems—they need to be reliable and adequately resourced.</p>
</li>
<li><p><strong>Recurring Subscriptions:</strong> Developer tools, SaaS platforms, and community management software form the backbone of your daily operations. Document these recurring expenses for predictable budgeting.</p>
</li>
</ul>
<p>By clearly identifying these fixed costs, you establish your minimum operating requirements—the baseline below which your DevRel function cannot effectively operate. This clarity helps protect essential resources when budget pressures arise.</p>
<h3 id="heading-variable-costs-mission-specific-expenditures">Variable Costs: Mission-Specific Expenditures</h3>
<p>Variable costs are like mission-specific equipment—they change based on your tactical objectives and operational tempo:</p>
<ul>
<li><p><strong>Event Participation:</strong> Calculate costs for conferences, meetups, and hackathons, including registration fees, booth expenses, demo equipment, travel, accommodations, and promotional materials. Remember that these costs scale with the number of events and team members attending.</p>
</li>
<li><p><strong>Content Production:</strong> Budget for content initiatives like video production, technical writing, design work, and promotional campaigns. High-quality content isn't cheap—but low-quality content is ultimately more expensive in terms of wasted resources and missed opportunities.</p>
</li>
<li><p><strong>Community Initiatives:</strong> Allocate funds for developer challenges, grants programs, ambassador incentives, and community awards. These investments often yield the highest ROI in terms of community engagement and platform advocacy.</p>
</li>
</ul>
<p>The variable nature of these expenses requires flexible budgeting approaches. Consider establishing separate budget envelopes for different types of activities, allowing your team to make tactical decisions within strategic guidelines—just as field commanders need appropriate autonomy within the mission parameters.</p>
<h3 id="heading-contingency-funds-strategic-reserves">Contingency Funds: Strategic Reserves</h3>
<p>No military operation proceeds exactly as planned, and neither will your DevRel initiatives. Contingency planning isn't pessimism—it's prudence:</p>
<ul>
<li><p><strong>Emergency Reserve:</strong> Set aside 10-15% of your total budget as unallocated funds to address unexpected challenges or opportunities. This isn't waste—it's your quick reaction capability.</p>
</li>
<li><p><strong>Scenario Planning:</strong> Develop financial responses to possible situations: What if a key event is canceled? What if a competitor launches an aggressive developer acquisition campaign? What if an unexpected technical issue requires additional documentation and support?</p>
</li>
<li><p><strong>Budget Defense:</strong> Documentation of your contingency planning demonstrates strategic foresight to financial stakeholders, making it easier to protect these crucial funds from being reallocated during budget reviews.</p>
</li>
</ul>
<p>Just as Rangers maintain reserve supplies and backup equipment, your financial planning should include provisions for when circumstances change or initial approaches prove insufficient.</p>
<hr />
<p>Cost considerations in DevRel aren't just about controlling expenses—they're about strategic resource allocation that maximizes impact while ensuring operational sustainability. By clearly distinguishing between fixed costs, variable expenses, and necessary contingencies, you create a financial framework that supports both immediate initiatives and long-term community building.</p>
<p>Remember that in both military logistics and DevRel budgeting, the goal isn't to spend as little as possible—it's to ensure that every dollar deployed advances your mission objectives. A well-resourced, strategically funded DevRel operation will consistently outperform competitors who treat developer relations as an afterthought or cost center rather than a strategic investment.</p>
<hr />
<h1 id="heading-conclusion">Conclusion</h1>
<blockquote>
<p>"Plans are worthless, but planning is everything." — General Dwight D. Eisenhower</p>
</blockquote>
<p>When Army Rangers complete a mission, they don't just pack up and move on. They conduct thorough After Action Reviews (AARs) to evaluate what worked, what didn't, and how to improve future operations. In that spirit, let's review what we've learned about applying military strategic planning to Developer Relations.</p>
<h2 id="heading-the-strategic-advantage">The Strategic Advantage</h2>
<p>The METT-TC framework isn't just another corporate planning tool—it's a battle-tested approach to operating in complex, uncertain environments. By adapting it for DevRel, you gain several distinct advantages:</p>
<p>First, you ensure comprehensive planning across all critical dimensions. Nothing falls through the cracks because the framework demands attention to each vital component—from mission clarity to resource allocation.</p>
<p>Second, you create alignment between tactical actions and strategic objectives. Every blog post, hackathon, or documentation update has a clear line of sight to your overarching mission, eliminating wasted effort and "random acts of DevRel."</p>
<p>Third, you develop the ability to anticipate challenges rather than merely reacting to them. By thoroughly analyzing your ecosystem and understanding your target market, you can prepare for potential obstacles before they materialize—just as Rangers prepare for multiple contingencies before deployment.</p>
<p>When combined with the Orbital Model for community engagement, METT-TC offers a powerful framework for not just attracting developers but systematically nurturing them from casual users to passionate advocates and contributors. You're not just building a user base—you're developing a force multiplier that extends your reach far beyond what your internal team could accomplish alone.</p>
<h2 id="heading-next-steps-for-devrel-professionals">Next Steps for DevRel Professionals</h2>
<p>As you prepare to implement this approach in your organization, consider these final directives:</p>
<h3 id="heading-customize-mett-tc">Customize METT-TC</h3>
<p>This framework isn't meant to be a rigid doctrine—it's an adaptable approach that should be tailored to your specific circumstances. A Web3 storage protocol will have different considerations than a payment processing API or a frontend framework. Modify the components as needed while maintaining the disciplined thinking that makes the framework effective.</p>
<h3 id="heading-collaborate-across-teams">Collaborate Across Teams</h3>
<p>No special operations unit succeeds in isolation, and neither will your DevRel strategy. Involve marketing to amplify your message, engineering to ensure product-community alignment, and product teams to close the feedback loop between developers and your platform. Your planning process should reflect the cross-functional nature of successful DevRel.</p>
<h3 id="heading-measure-and-adapt">Measure and Adapt</h3>
<p>Set clear metrics for each component of your strategy and establish regular review cycles. Like the best military units, you should be your own toughest critic, constantly seeking to improve based on real-world feedback rather than assumptions or wishful thinking.</p>
<h3 id="heading-invest-in-relationships">Invest in Relationships</h3>
<p>At its core, Developer Relations is about humans connecting with humans. While this framework provides the strategic foundation, never lose sight of the fact that genuine relationships—built on trust, value, and mutual respect—are your ultimate objective. The most meticulously planned strategy will fail if it doesn't foster authentic connections with your developer community.</p>
<h2 id="heading-remember">Remember</h2>
<p>In DevRel, as in any mission-critical operation, success comes from detailed planning, adaptability, and a deep understanding of both your objectives and the environment in which you operate. The best plans can't anticipate every challenge, but the planning process itself develops the mental agility and situational awareness needed to overcome unexpected obstacles.</p>
<p>By planning the Army Ranger way with METT-TC, you're not just organizing activities—you're developing a mindset that separates elite DevRel operations from the merely adequate. You're declaring that developer community building is too important to leave to chance or ad hoc efforts. And most importantly, you're setting yourself and your developer community up for sustained success in even the most competitive technological battlefields.</p>
<p>The mission is clear. The troops are ready. The plan is solid.</p>
<p>Now it's time to execute.</p>
]]></content:encoded></item><item><title><![CDATA[Begin Tutorial: Un Tutorial Interactivo]]></title><description><![CDATA[En este tutorial, recorrerá pasos interactivos que le ayudarán a profundizar su conocimiento y comprensión del entorno de AOS.

"Bajar Por La Madriguera Del Conejo"En este divertido ejercicio, encontrarás una serie de desafíos presentados por dos per...]]></description><link>https://blog.patrickskinner.tech/begin-tutorial-un-tutorial-interactivo-de-ao</link><guid isPermaLink="true">https://blog.patrickskinner.tech/begin-tutorial-un-tutorial-interactivo-de-ao</guid><category><![CDATA[Arweave]]></category><category><![CDATA[decentralization]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Web3]]></category><category><![CDATA[decentralized storage]]></category><category><![CDATA[decentralized compute]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Fri, 12 Apr 2024 01:01:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1712850193920/247e5fba-6ff5-433e-8614-819b338044f9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En este tutorial, recorrerá pasos interactivos que le ayudarán a profundizar su conocimiento y comprensión del entorno de <a target="_blank" href="https://tinyurl.com/aothecomputer">AOS</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712617058273/c1e71341-24bc-4929-b0d7-8ebd52101751.png" alt class="image--center mx-auto" /></p>
<details><summary>"Bajar Por La Madriguera Del Conejo"</summary><div data-type="detailsContent">En este divertido ejercicio, encontrarás una serie de desafíos presentados por dos personajes familiares, Morfeo y Trinity. Te sumergirás profundamente <code>bajar por la madriguera del conejo</code> por Morpheus mientras te presenta una serie de desafíos para demostrar que eres <code>El Elegido</code>. Una vez que hayas completado todos los desafíos presentados por Morpheus y Trinity, recibirás un token que te otorga acceso a una sala de chat exclusiva dentro de AO llamada "The Construct".</div></details>

<hr />
<h2 id="heading-preparativos">Preparativos</h2>
<details><summary>La Historia</summary><div data-type="detailsContent">Siempre has sabido que hay algo más en este mundo, justo más allá de tu alcance. Has estado buscándolo, sin siquiera saber qué era eso que buscabas. Eso... es AO. Iniciamos nuestro viaje instalando el cliente de AOS y comenzando un nuevo proceso. Esto nos permitirá interactuar con el ordenador AO y completar el resto del tutorial.</div></details>

<h3 id="heading-requisitos-del-sistema">Requisitos del Sistema</h3>
<p>El cliente local de aos es muy sencillo de instalar. Solo asegúrate de tener:</p>
<ul>
<li><p>NodeJS versión 20 o superior. (Si aún no lo has instalado, consulta <a target="_blank" href="https://nodejs.org/en/download/package-manager">esta página</a> para encontrar instrucciones para tu sistema operativo).</p>
</li>
<li><p>Un editor de código de tu elección.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Aunque no es obligatorio, recomendamos instalar el complemento de AO en tu editor de texto preferido para optimizar tu experiencia con AOS.</div>
</div>

<h3 id="heading-instalando-aos">Instalando AOS</h3>
<p>Una vez que tengas NodeJS en tu máquina, todo lo que necesitas hacer es instalar AOS y ejecutarlo:</p>
<pre><code class="lang-bash">npm i -g https://get_ao.g8way.io
</code></pre>
<p>Después de la instalación, ¡simplemente podemos ejecutar el comando en sí para iniciar un nuevo proceso AOS!</p>
<pre><code class="lang-bash">aos
</code></pre>
<h3 id="heading-bienvenido-a-la-madriguera-del-conejo">Bienvenido a la madriguera del conejo</h3>
<p>La utilidad que acabas de iniciar es un cliente local, listo para retransmitir mensajes para ti a tu nuevo proceso dentro del ordenador AO.</p>
<p>Una vez conectado, deberías ver lo siguiente:</p>
<pre><code class="lang-bash">_____                   _______                   _____
         /\    \                 /::\    \                 /\    \
        /::\    \               /::::\    \               /::\    \
       /::::\    \             /::::::\    \             /::::\    \
      /::::::\    \           /::::::::\    \           /::::::\    \
     /:::/\:::\    \         /:::/~~\:::\    \         /:::/\:::\    \
    /:::/__\:::\    \       /:::/    \:::\    \       /:::/__\:::\    \
   /::::\   \:::\    \     /:::/    / \:::\    \      \:::\   \:::\    \
  /::::::\   \:::\    \   /:::/____/   \:::\____\   ___\:::\   \:::\    \
 /:::/\:::\   \:::\    \ |:::|    |     |:::|    | /\   \:::\   \:::\    \
/:::/  \:::\   \:::\____\|:::|____|     |:::|    |/::\   \:::\   \:::\____\
\::/    \:::\  /:::/    / \:::\    \   /:::/    / \:::\   \:::\   \::/    /
 \/____/ \:::\/:::/    /   \:::\    \ /:::/    /   \:::\   \:::\   \/____/
          \::::::/    /     \:::\    /:::/    /     \:::\   \:::\    \
           \::::/    /       \:::\__/:::/    /       \:::\   \:::\____\
           /:::/    /         \::::::::/    /         \:::\  /:::/    /
          /:::/    /           \::::::/    /           \:::\/:::/    /
         /:::/    /             \::::/    /             \::::::/    /
        /:::/    /               \::/____/               \::::/    /
        \::/    /                 ~~                      \::/    /
         \/____/                                           \/____/

ao Operating System

aos - 1.8.9
2024 - Type <span class="hljs-string">".exit"</span> to <span class="hljs-built_in">exit</span>
aos process:  1xM1_lDZ428sJHpTX7rtcR6SrDubyRVO06JEEWs_eWo
</code></pre>
<p>Repasemos la impresión inicial después de ejecutar AOS:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712621331167/613d1dab-2402-45a8-bdda-44c97590c4ef.png" alt class="image--center mx-auto" /></p>
<p>Después de ejecutar AOS en tu terminal, deberías ver:</p>
<ul>
<li><p>Una imagen de arte ASCII de AOS.</p>
</li>
<li><p>Un mensaje de bienvenida.</p>
</li>
<li><p>La versión de AOS que estás ejecutando.</p>
</li>
<li><p>Un mensaje instructivo para salir.</p>
</li>
<li><p>Tu ID de proceso.</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Si la versión de tu SO es diferente a la última versión, aparecerá un mensaje preguntándote si deseas actualizar la versión. De ser así, simplemente sal del proceso presionando "Ctrl+C" dos veces, ejecuta <code>npm i -g </code><a target="_blank" href="https://get_ao.g8way.io"><code>https://get_ao.g8way.io</code></a> para actualizar, y luego ejecuta <code>aos</code> nuevamente.</div>
</div>

<p>¡Bienvenido a tu nuevo hogar en el ordenador AO! El prompt que ahora estás viendo es tu servidor personal en esta máquina descentralizada.</p>
<p>Ahora, profundicemos más en la madriguera del conejo explorando uno de los dos conceptos clave de AO: la Mensajería.</p>
<hr />
<h2 id="heading-mensajeria-en-ao">Mensajería en <code>ao</code></h2>
<h3 id="heading-aprende-como-los-mensajes-otorgan-a-ao-la-capacidad-de-computo-paralelo">Aprende cómo los Mensajes otorgan a <code>AO</code> la Capacidad de Cómputo Paralelo</h3>
<p>En <code>AO</code>, cada proceso se ejecuta en paralelo, creando un entorno altamente escalable. Las llamadas directas a funciones entre procesos no son factibles porque cada proceso opera de manera independiente y asincrónica.</p>
<p>La mensajería aborda esto al habilitar la comunicación asincrónica. Los procesos envían y reciben mensajes en lugar de invocar directamente funciones entre sí. Este método permite una interacción flexible y eficiente, donde los procesos pueden responder a mensajes, mejorando la escalabilidad y la capacidad de respuesta del sistema.</p>
<p>Comenzaremos explorando los fundamentos de la mensajería en <code>AOS</code>, cómo ver los mensajes recibidos en tu bandeja de entrada y cómo enviar mensajes a otros procesos.</p>
<h3 id="heading-tutorial-en-video">Tutorial en Video</h3>
<p>(Solo en ingles)</p>
<iframe width="680" height="350" src="https://www.youtube.com/embed/6aCjKK6F1yQ?si=3Ny7U-GgyNsRWlXS"></iframe>

<h3 id="heading-paso-1-entender-la-estructura-del-mensaje">Paso 1: Entender la Estructura del Mensaje</h3>
<ul>
<li><p><strong>Fundamentos del Mensaje:</strong> Los mensajes en <code>AO</code> se construyen usando tablas Lua, que son estructuras de datos versátiles que pueden contener múltiples valores. Dentro de estas tablas, el campo "Data" es crucial ya que contiene el contenido o carga útil del mensaje. Esta estructura permite el envío y recepción eficiente de información entre procesos, mostrando cómo los primitivos de <code>AO</code> aprovechan las capacidades subyacentes de Arweave para facilitar operaciones complejas y componibles.</p>
<p>  Para especificaciones detalladas, por favor refiérete a la documentación original en la <a target="_blank" href="https://specs.g8way.io/?tx=xwOgX-MmqN5_-Ny_zNu2A8o-PnTGsoRb_3FrtiMAkuw">página de especificaciones de G8way</a>.</p>
</li>
<li><p><strong>Ejemplo</strong>: <code>{ Data = "¡Hola desde el Proceso A!" }</code> es un mensaje simple.</p>
</li>
</ul>
<h3 id="heading-paso-2-abrir-la-cli-de-aos">Paso 2: Abrir la CLI de AOS</h3>
<ul>
<li>Lanza la interfaz de línea de comandos (CLI) de AOS escribiendo <code>aos</code> en tu terminal y presionando Enter.</li>
</ul>
<pre><code class="lang-sh">aos
</code></pre>
<h3 id="heading-paso-3-como-enviar-un-mensaje">Paso 3: Cómo Enviar un Mensaje</h3>
<pre><code class="lang-sh">  Send({ Target = <span class="hljs-string">"ID del proceso"</span>, Data = <span class="hljs-string">"Hola Mundo!"</span> })
</code></pre>
<ul>
<li><p><strong>Enviar</strong>: La función <code>Send</code> está globalmente disponible en el entorno interactivo de AOS.</p>
</li>
<li><p><strong>Target</strong>: Para enviar un mensaje a un proceso específico, incluye un campo <code>Target</code> en tu mensaje.</p>
</li>
<li><p><strong>Data</strong>: El <code>Data</code> es el mensaje de texto que deseas sea recibido por el proceso receptor. En este ejemplo, el mensaje es "¡Hola Mundo!".</p>
</li>
</ul>
<h3 id="heading-paso-4-almacenar-el-id-del-proceso-de-morpheus">Paso 4: Almacenar el ID del Proceso de <code>Morpheus</code></h3>
<p>Usaremos el ID del proceso proporcionado a continuación y lo almacenaremos como una variable llamada Morpheus.</p>
<pre><code class="lang-sh">P2RS2VtQ4XtYEvAXYDulEA9pCBCIRpJDcakTR9aW434
</code></pre>
<p>Copia el ID del proceso anterior y almacénalo como una variable ejecutando el siguiente comando en la CLI de AOS:</p>
<pre><code class="lang-sh">Morpheus = <span class="hljs-string">"P2RS2VtQ4XtYEvAXYDulEA9pCBCIRpJDcakTR9aW434"</span>
</code></pre>
<p>Esto almacenará el ID del proceso como una variable llamada <code>Morpheus</code>, facilitando la interacción con el ID de proceso específico.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Al crear la variable <code>Morpheus</code>, la única respuesta que deberías ver es <code>undefined</code>. Esto es esperado. Para verificar si la variable fue creada con éxito, escribe <code>Morpheus</code> y presiona Enter. Deberías ver el ID del proceso que almacenaste.</div>
</div>

<h3 id="heading-verificar-la-variable-morpheus">Verificar la Variable <code>Morpheus</code></h3>
<pre><code class="lang-sh"><span class="hljs-comment"># Verifica la variable Morpheus escribiendo `Morpheus`</span>
 Morpheus
<span class="hljs-comment"># Resultados Esperados:</span>
P2RS2VtQ4XtYEvAXYDulEA9pCBCIRpJDcakTR9aW434


<span class="hljs-comment"># Si se devuelve `undefined`,</span>
<span class="hljs-comment"># entonces</span>

 la variable no se creó con éxito.
</code></pre>
<h3 id="heading-paso-5-enviar-un-mensaje-a-morpheus">Paso 5: Enviar un Mensaje a Morpheus</h3>
<p>Después de obtener el ID del proceso de Morpheus y almacenarlo en una variable, estás listo para comunicarte con él. Para hacer esto, usas la función Send. Morpheus, él mismo, es un proceso paralelo que se ejecuta en ao. Recibe y envía mensajes usando una serie de Handlers. Enviémosle un mensaje y veamos qué sucede.</p>
<pre><code class="lang-lua">Send({ Target = Morpheus, Data = "Morpheus?" })
</code></pre>
<ul>
<li><p>Tu <code>Target</code> es <code>Morpheus</code>, que es la variable que definimos anteriormente usando el ID del proceso de <code>Morpheus</code>.</p>
</li>
<li><p>El <code>Data</code> es el mensaje que quieres enviar a Morpheus. En este caso, es "Morpheus?".</p>
</li>
</ul>
<p><strong>Resultados Esperados:</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Tu Comando de Mensaje</span>
Send({ Target = Morpheus, Data = <span class="hljs-string">"Morpheus?"</span>})
<span class="hljs-comment"># Mensaje añadido a la bandeja de salida</span>
message added to outbox
<span class="hljs-comment"># Se recibe un Nuevo Mensaje del ID del proceso de `Morpheus`</span>
New Message From BWM...ulw: Data = I am here. You are f
</code></pre>
<p>Has enviado un mensaje a Morpheus y recibido una respuesta, pero no puedes leer el mensaje completo. Aprendamos sobre la <code>Bandeja de Entrada</code> y cómo leer mensajes.</p>
<h3 id="heading-paso-6-la-bandeja-de-entrada">Paso 6: La Bandeja de Entrada</h3>
<p>La <code>Bandeja de Entrada</code> es donde recibes mensajes de otros procesos. ::: info Para ver una visión más profunda de un mensaje de la bandeja de entrada, dirígete a la página de Conceptos de <a target="_blank" href="../../concepts/messages">Mensajes</a>. :::</p>
<p>Verifiquemos tu bandeja de entrada para ver cuántos mensajes has recibido.</p>
<p>Dentro de tu CLI de aos, escribe el siguiente comando:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#Inbox</span>
</code></pre>
<p>Si estás siguiendo activamente el tutorial, la bandeja de entrada no tendrá muchos mensajes. Sin embargo, si has estado experimentando con el entorno de aos, puedes tener más de 1 mensaje en tu bandeja de entrada.</p>
<p><strong>Ejemplo de Retorno:</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Tu Comando de Bandeja de Entrada</span>
 <span class="hljs-comment">#Inbox</span>
<span class="hljs-comment"># El comando devolverá el número de mensajes en tu bandeja de entrada.</span>
4
</code></pre>
<p>En el ejemplo anterior, el retorno es <code>4</code>, indicando que hay cuatro mensajes en la bandeja de entrada.</p>
<p>Como estamos buscando activamente la respuesta de <code>Morpheus</code>, asumiremos que su mensaje fue el último recibido. Para leer el último mensaje en tu bandeja de entrada, escribe el siguiente comando:</p>
<pre><code class="lang-sh"> Inbox[<span class="hljs-comment">#Inbox].Data</span>
</code></pre>
<p>Este comando te permite aislar el Data del mensaje y solo leer el contenido de los datos.</p>
<p>El Retorno Esperado:</p>
<pre><code class="lang-sh"><span class="hljs-comment"># Tu Comando de Bandeja[x].Data</span>
Inbox[<span class="hljs-comment">#Inbox].Data</span>
<span class="hljs-comment"># El comando devolverá el `Data` del mensaje.</span>
<span class="hljs-comment"># Data es lo que usualmente representa el mensaje basado en texto recibido de un proceso a otro.</span>
I am here. You are finally awake. Are you ready to see how far the rabbit hole goes?
</code></pre>
<p>Ahora estás usando tu propio proceso para comunicarte con Morpheus, otro proceso paralelo que se ejecuta en AO. Estás listo para avanzar al siguiente paso en el tutorial.</p>
<h3 id="heading-paso-7-enviar-mensajes-con-etiquetas">Paso 7: Enviar Mensajes con Etiquetas</h3>
<p><strong>Propósito de las Etiquetas</strong>: Las etiquetas en los mensajes de AOS se usan para categorizar, dirigir y procesar mensajes de manera eficiente. Juegan un papel crucial en el manejo de mensajes, especialmente cuando se trata de múltiples procesos o flujos de trabajo complejos.</p>
<p>Algunos procesos usan <code>Handlers</code> que interactúan específicamente con mensajes que tienen ciertas etiquetas. Por ejemplo, un proceso puede tener un manejador que solo interactúa con mensajes que tienen una etiqueta específica, lo cual veremos un ejemplo de en el tutorial de <a target="_blank" href="chatroom">chatroom</a>.</p>
<h4 id="heading-como-usar-etiquetas-en-mensajes">Cómo Usar Etiquetas en Mensajes</h4>
<p>En el caso de Morpheus, podemos usar etiquetas para categorizar nuestros mensajes, y dado que Morpheus es un proceso autónomo, tiene manejadores que pueden interactuar con mensajes que tienen ciertas etiquetas.</p>
<h5 id="heading-anadiendo-etiquetas-a-un-mensaje"><strong>Añadiendo Etiquetas a un Mensaje</strong>:</h5>
<ul>
<li>Ya sabemos que el <code>Data</code> de un mensaje es el mensaje basado en texto que quieres enviar a otro proceso. Anteriormente, enviamos un mensaje a Morpheus sin ninguna etiqueta, en el cual él usó un manejador para responder a un data que coincidía exactamente.</li>
</ul>
<h4 id="heading-demostremos-a-morpheus-que-estamos-listos">Demostremos a Morpheus Que Estamos Listos</h4>
<p>Envía a Morpheus un mensaje con la etiqueta <code>Action</code> y el valor <code>rabbithole</code>.</p>
<p><strong>Ejemplo:</strong></p>
<pre><code class="lang-lua">Send({ Target = Morpheus, Data = "Code: rabbithole", Action = "Unlock" })
</code></pre>
<p><strong>Resultados Esperados:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848698893/5c13de6c-8f1a-4206-84bd-65f5ab6f4e06.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-consejos-adicionales-para-usar-etiquetas">Consejos Adicionales para Usar Etiquetas</h3>
<ul>
<li><p><strong>Etiquetado Consistente</strong>: Desarrolla un sistema de etiquetado consistente para tu aplicación para hacer el manejo de mensajes más predecible.</p>
</li>
<li><p><strong>Nombramiento de Etiquetas</strong>: Elige nombres claros y descriptivos para tus etiquetas. Esto facilita entender el propósito y contexto de los mensajes a primera vista.</p>
</li>
<li><p><strong>Seguridad con Etiquetas</strong>: Recuerda que las etiquetas no están encriptadas ni ocultas, así que evita usar información sensible como etiquetas.</p>
</li>
</ul>
<h3 id="heading-uso-avanzado-de-etiquetas">Uso Avanzado de Etiquetas</h3>
<ul>
<li><strong>Gestión de Flujos de Trabajo</strong>: Las etiquetas pueden ser instrumentales en la gestión de flujos de trabajo, especialmente en sistemas donde los mensajes pasan por múltiples etapas o procesos.</li>
</ul>
<h3 id="heading-consejos-adicionales-para-la-mensajeria">Consejos Adicionales para la Mensajería</h3>
<ul>
<li><p><strong>Estructura del Mensaje</strong>: Explora otros campos como <code>Epoch</code>, <code>From</code> y <code>Nonce</code> para necesidades de mensajería más complejas.</p>
</li>
<li><p><strong>Depuración</strong>: Usa la función <a target="_blank" href="/concepts/tour.html#_6-data-representation-with-dump"><code>Dump</code></a> para imprimir mensajes para depuración.</p>
</li>
<li><p><strong>Consideraciones de Seguridad</strong>: Ten cuidado con el contenido y manejo de los mensajes, y nunca envíes nada considerado privado o sensible.</p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusión</h3>
<p>Ahora has aprendido cómo enviar mensajes con etiquetas, lo cual es una herramienta poderosa para categorizar y dirigir mensajes en AOS.</p>
<p>Morpheus te ha invitado oficialmente a la siguiente etapa de tu viaje. Estás listo para avanzar al siguiente paso en el tutorial, <a target="_blank" href="chatroom">Creando un Chatroom</a>.</p>
<hr />
<h2 id="heading-crear-una-sala-de-chat">Crear una Sala de Chat</h2>
<p>Mis disculpas por eso, aquí tienes la traducción corregida sin modificar el contenido dentro de los bloques de código:</p>
<h3 id="heading-construyendo-un-chatroom-en-aos">Construyendo un Chatroom en aos</h3>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Si has encontrado el deseo de aprender cómo crear un chatroom dentro de <code>ao</code>, eso significa que entendemos al menos la metodología básica de enviar y recibir mensajes. Si no, se sugiere que revises el tutorial de Mensajería antes de continuar.</div>
</div>

<p>En este tutorial, estaremos construyendo un chatroom dentro de <code>AO</code> utilizando el lenguaje de scripting Lua. El chatroom contará con dos funciones principales:</p>
<ol>
<li><p><strong>Registrar</strong>: Permite que los procesos se unan al chatroom.</p>
</li>
<li><p><strong>Transmitir</strong>: Envía mensajes de un proceso a todos los participantes registrados.</p>
</li>
</ol>
<p>Comencemos estableciendo la base para nuestro chatroom.</p>
<h3 id="heading-tutorial-en-video-1">Tutorial en Video</h3>
<p>(Solo en Ingles)</p>
<iframe width="680" height="350" src="https://www.youtube.com/embed/oPCx-cfubF0?si=D5yWxmyFMV-4mh2P"></iframe>

<h3 id="heading-paso-1-la-base">Paso 1: La Base</h3>
<ul>
<li>Abre tu editor de código preferido.</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Puede que encuentres útil tener las <a target="_blank" href="../../references/editor-setup.md">Extensiones Recomendadas</a> instaladas en tu editor de código para mejorar tu experiencia de scripting en Lua.</div>
</div>

<ul>
<li>Crea un nuevo archivo nombrado <code>chatroom.lua</code>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848801698/c6968562-d364-4a17-9d73-bd1bc379d6ba.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-paso-2-creando-la-lista-de-miembros">Paso 2: Creando La Lista de Miembros</h3>
<ul>
<li>En <code>chatroom.lua</code>, comenzarás inicializando una lista para rastrear a los participantes:</li>
</ul>
<pre><code class="lang-lua">Members = Members or {}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848839201/e568daeb-f2e1-4c2c-a82b-64dac7ae0cfb.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Guarda el archivo <code>chatroom.lua</code></li>
</ul>
<h3 id="heading-paso-3-cargar-el-chatroom-en-aos">Paso 3: Cargar el Chatroom en aos</h3>
<p>Con <code>chatroom.lua</code> guardado, ahora cargarás el chatroom en <code>AOS</code>.</p>
<ul>
<li><p>Si aún no lo has hecho, inicia tu <code>AOS</code> en tu terminal dentro del directorio donde se guarda chatroom.lua</p>
</li>
<li><p>En la CLI de <code>AOS</code>, escribe el siguiente script para incorporar tu script en el proceso <code>AOS</code>:</p>
</li>
</ul>
<pre><code class="lang-lua">.load chatroom.lua
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848863034/7b0ad11c-0f14-4030-9c40-6766d4594488.png" alt class="image--center mx-auto" /></p>
<p>Como muestra la captura de pantalla anterior, puedes recibir <code>undefined</code> como respuesta. Esto es esperado, pero aún queremos asegurarnos de que el archivo se cargó correctamente.</p>
<p>::: info En el entorno de evaluación Lua de AOS, cuando ejecutas un fragmento de código que no devuelve explícitamente un valor, <code>undefined</code> es una respuesta estándar, indicando que no se devolvió ningún resultado. Esto se puede observar al cargar recursos o ejecutar operaciones. Por ejemplo, ejecutar <code>X = 1</code> producirá <code>undefined</code> porque la declaración no incluye una sentencia de retorno.</p>
<p>Sin embargo, si ejecutas <code>X = 1; return X</code>, el entorno devolverá el valor <code>1</code>. Este comportamiento es esencial para entender cuando trabajas dentro de este marco, ya que ayuda a aclarar la distinción entre ejecutar comandos que modifican el estado frente a aquellos destinados a producir una salida directa. :::</p>
<ul>
<li>Escribe <code>Members</code>, o como hayas nombrado tu lista de usuarios, en <code>AOS</code>. Debería devolver un array vacío <code>{ }</code>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848890030/fe12bb35-3d75-4288-a38e-95d43f714882.png" alt class="image--center mx-auto" /></p>
<p>Si ves un array vacío, entonces tu script ha sido cargado con éxito en <code>AOS</code>.</p>
<h3 id="heading-paso-4-creando-funcionalidades-del-chatroom">Paso 4: Creando Funcionalidades del Chatroom</h3>
<h4 id="heading-el-manejador-de-registro">El Manejador de Registro</h4>
<p>El manejador de registro permitirá que los procesos se unan al chatroom.</p>
<ol>
<li><strong>Añadiendo un Manejador de Registro:</strong> Modifica <code>chatroom.lua</code> para incluir un manejador para que <code>Members</code> se registren en el chatroom con el siguiente código:</li>
</ol>
<pre><code class="lang-lua">Handlers.add(
  "Register",
  Handlers.utils.hasMatchingTag("Action", "Register"),
  function (msg)
    table.insert(Members, msg.From)
    Handlers.utils.reply("registered")(msg)
  end
)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848915684/8dd809fc-2e72-4c13-b0f3-0ceabb3a50fd.png" alt class="image--center mx-auto" /></p>
<p>Este manejador permitirá que los procesos se registren en el chatroom respondiendo a la etiqueta <code>Action = "Register"</code>. Un mensaje impreso confirmará diciendo <code>registered</code> aparecerá cuando el registro sea exitoso.</p>
<ol start="2">
<li><strong>Recargar y Probar:</strong> Vamos a recargar y probar el script registrándonos en el chatroom.</li>
</ol>
<ul>
<li><p>Guarda y recarga el script en AOS usando <code>.load chatroom.lua</code>.</p>
</li>
<li><p>Verifica si el manejador de registro se cargó con el siguiente script:</p>
</li>
</ul>
<pre><code class="lang-lua">Handlers.list
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848932349/9a19db91-7d4d-43c2-bef8-baec84e3677e.png" alt class="image--center mx-auto" /></p>
<p>Esto devolverá una lista de todos los manejadores en el chatroom. Dado que esta es probablemente tu primera vez desarrollando en <code>AOS</code>, solo deberías ver un manejador con el nombre <code>Register</code>.</p>
<ul>
<li>Probemos el proceso de registro registrándonos en el chatroom:</li>
</ul>
<pre><code class="lang-lua">Send({ Target = ao.id, Action = "Register" })
</code></pre>
<p>Si es exitoso, deberías ver que había un <code>mensaje añadido a tu bandeja de salida</code> y que luego ves un nuevo mensaje impreso que dice <code>registrado</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848950711/6dac38f9-0795-48a0-bbf7-eae9c5a4424a.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Finalmente, verifiquemos si fuimos agregados exitosamente a la lista de <code>Members</code>:</li>
</ul>
<pre><code class="lang-lua">Members
</code></pre>
<p>Si es exitoso, ahora verás tu ID de proceso en la lista de <code>Members</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712848961803/85f22f27-e5cd-4e7a-94d2-5d2b096c9fc5.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-anadiendo-un-manejador-de-transmision">Añadiendo un Manejador de Transmisión</h4>
<p>Ahora que tienes un chatroom, creemos un manejador que te permita transmitir mensajes a todos los miembros del chatroom.</p>
<ul>
<li>Añade el siguiente manejador al archivo <code>chatroom.lua</code>:</li>
</ul>
<pre><code class="lang-lua">Handlers.add(
  "Broadcast",
  Handlers.utils.hasMatchingTag("Action", "Broadcast"),
  function (msg)
    for _, recipient in ipairs(Members) do
      ao.send({Target = recipient, Data = msg.Data})
    end
    Handlers.utils.reply("Broadcasted.")(msg)
  end
)
</code></pre>
<p>Este manejador te permitirá transmitir mensajes a todos los miembros del chatroom.</p>
<ul>
<li><p>Guarda y recarga el script en AOS usando <code>.load chatroom.lua</code>.</p>
</li>
<li><p>Probemos el manejador de transmisión enviando un mensaje al chatroom:</p>
</li>
</ul>
<pre><code class="lang-lua">Send({Target = ao.id, Action = "Broadcast", Data = "Broadcasting My 1st Message" })
</code></pre>
<ul>
<li>Si es exitoso, deberías ver que había un <code>mensaje añadido a tu bandeja de salida</code> y que luego ves un nuevo mensaje impreso que dice <code>Broadcasting My 1st Message</code> porque también eres un destinatario de este mensaje ya que eres miembro del chatroom <code>Members</code>.</li>
</ul>
<h3 id="heading-paso-5-invitando-a-morpheus-al-chatroom">Paso 5: Invitando a Morpheus al Chatroom</h3>
<p>Ahora que te has registrado con éxito en el chatroom, invitemos a Morpheus a unirse a nosotros. Para hacer esto, le enviaremos una invitación que le permitirá registrarse en el chatroom.</p>
<p>Morpheus es un agente autónomo con un manejador que responderá a la etiqueta <code>Action = "Join"</code>, en la cual luego usará tu etiqueta <code>Register</code> para registrarse en el chatroom.</p>
<ul>
<li>Enviemos a Morpheus una invitación para unirse al chatroom:</li>
</ul>
<pre><code class="lang-lua">Send({ Target = Morpheus, Action = "Join" })
</code></pre>
<ul>
<li>Para confirmar que Morpheus se ha unido al chatroom, verifica la lista de <code>Members</code>:</li>
</ul>
<pre><code class="lang-lua">Members
</code></pre>
<p>Si es exitoso, recibirás un mensaje difundido de Morpheus.</p>
<h3 id="heading-paso-6-invitando-a-trinity-al-chatroom">Paso 6: Invitando a Trinity al Chatroom</h3>
<p>Dentro de este mensaje, él te dará el ID del proceso de Trinity y te dirá que la invites al chatroom.</p>
<p>Usa los mismos procesos para guardar su ID del proceso como <code>Trinity</code> y para invitarla al chatroom como hiciste con Morpheus.</p>
<p>Si ella se une con éxito al chatroom, entonces planteará el siguiente desafío para ti, creando un <a target="_blank" href="token">token</a>.</p>
<h3 id="heading-involucrando-a-otros-en-el-chatroom">Involucrando a Otros en el Chatroom</h3>
<h4 id="heading-incorporando-a-otros">Incorporando a Otros</h4>
<ul>
<li><p>Invita a Usuarios de AOS: Anima a otros usuarios de AOS a unirse a tu chatroom. Pueden registrarse y participar en la transmisión.</p>
</li>
<li><p>Proporciona Instrucciones de Incorporación: Comparte un script simple con ellos para una incorporación fácil:</p>
</li>
</ul>
<pre><code class="lang-lua">-- ¡Hey, vamos a chatear en aos! Únete a mi chatroom enviando este comando en tu entorno aos:
Send({ Target = [Tu ID de Proceso], Action = "Register" })
-- Luego, puedes transmitir mensajes usando:
Send({Target = [Tu ID de Proceso], Action = "Broadcast", Data = "Tu Mensaje" })
</code></pre>
<h3 id="heading-proximos-pasos">Próximos Pasos</h3>
<p>¡Felicidades! Has construido con éxito un chatroom en <code>AO</code> e has invitado a Morpheus a unirte. También has creado un manejador de transmisión para enviar mensajes a todos los miembros del chatroom.</p>
<p>A continuación, continuarás interactuando con Morpheus, pero esta vez agregarás a Trinity a la conversación. Ella te guiará a través del próximo conjunto de desafíos. ¡Buena suerte!</p>
<hr />
<h2 id="heading-crear-un-token">Crear un Token</h2>
<p>::: info Profundizando en <code>AO</code>, ahora estás listo para crear tu propio token, un símbolo de valor e intercambio dentro de este medio descentralizado. Si tienes ganas de aprender cómo crear un token, pero no has visitado las lecciones de <a target="_blank" href="messaging">Mensajería</a> y <a target="_blank" href="chatroom">Construir un Chatroom</a>, asegúrate de hacerlo ya que esta página es parte de un tutorial interactivo de varias partes. :::</p>
<p>Al crear tokens, continuaremos usando el <a target="_blank" href="../../references/lua.md">Lenguaje Lua</a> dentro de <code>AO</code> para acuñar un token, guiados por los principios descritos en la <a target="_blank" href="../../references/token.md">Especificación de Token</a>.</p>
<h3 id="heading-continuando-por-la-madriguera-del-conejo">Continuando por la Madriguera del Conejo</h3>
<p>En nuestro último tutorial, <a target="_blank" href="chatroom">Construir un Chatroom</a>, aprendimos cómo crear un chatroom dentro de <code>AO</code>, invitamos a <code>Morpheus</code> y a <code>Trinity</code> al chatroom que creamos, y luego <code>Trinity</code> nos ha pedido que creemos un token para ella como una forma de demostrar que somos dignos de continuar por la madriguera del conejo.</p>
<p><strong>Comencemos.</strong></p>
<h3 id="heading-los-dos-caminos-para-construir-un-token">Los Dos Caminos para Construir un Token</h3>
<p>Hay dos caminos a seguir cuando construyes un token:</p>
<ol>
<li><p><strong>El Plano</strong>: Este es una plantilla prediseñada que te ayuda a construir rápidamente un token en <code>AO</code>. Es una excelente manera de comenzar y se puede personalizar para satisfacer tus necesidades.</p>
<p> Consulta aquí para aprender más sobre el <a target="_blank" href="../../guides/aos/blueprints/token.md">Plano de Token</a>.</p>
</li>
<li><p><strong>El Método Manual</strong>: Esta es una guía paso a paso para construir un token en <code>AO</code> desde cero. Este camino es para aquellos que quieren entender los mecanismos internos de un token y cómo construir uno desde el principio.</p>
<p> Consulta aquí para revisar la guía completa <a target="_blank" href="../../guides/aos/token.md">Construir un Token</a>.</p>
</li>
</ol>
<h3 id="heading-el-metodo-del-plano">El Método del Plano</h3>
<p>Para este tutorial, utilizaremos el Plano de Token para crear un token para <code>Trinity</code>. Este es una plantilla prediseñada que te ayuda a construir rápidamente un token en <code>AO</code>.</p>
<h4 id="heading-como-usar-el-plano-de-token">Cómo Usar el Plano de Token</h4>
<ol>
<li><p>Asegúrate de estar en el mismo directorio que antes durante los pasos anteriores del tutorial.</p>
</li>
<li><p>Abre el Terminal.</p>
</li>
<li><p>Inicia tu proceso <code>AOS</code>.</p>
</li>
<li><p>Escribe <code>.load-blueprint token</code></p>
</li>
</ol>
<p>Esto cargará los manejadores requeridos para el token del tutorial dentro de <code>AO</code>. Es importante notar que el plano del token no es específico de este tutorial y se puede usar como una base para cualquier token que desees crear.</p>
<h4 id="heading-verificar-que-el-plano-esta-cargado">Verificar que el Plano Está Cargado</h4>
<p>Escribe <code>Handlers.list</code> para ver los nuevos manejadores cargados.</p>
<p>Deberías ver una nueva lista de manejadores que han sido cargados en tu proceso <code>AOS</code>. Si has seguido los pasos anteriores del tutorial, también deberías ver los manejadores de tu chatroom.</p>
<p><strong>Ejemplo:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712849218971/5362c1ab-361c-40ff-9783-95a30deff7b4.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-probando-el-token">Probando el Token</h4>
<p>Ahora que el plano del token está cargado, podemos probar el token enviando un mensaje a nosotros mismos usando la etiqueta <code>Action = "Info"</code>.</p>
<pre><code class="lang-sh">Send({ Target = ao.id, Action = <span class="hljs-string">"Info"</span> })
</code></pre>
<p>Esto imprimirá un mensaje en la consola, pero para leer el mensaje, necesitaremos llamar a <code>.Data</code> del último mensaje.</p>
<pre><code class="lang-bash">Inbox[<span class="hljs-comment">#Inbox].Data</span>

<span class="hljs-comment"># Reemplaza #Inbox con el numero del ultimo mensaje recibido.</span>
</code></pre>
<p>Esto imprimirá la información del token en la consola. Debería mostrar tu ID de proceso con el saldo total de tokens disponibles.</p>
<h4 id="heading-enviando-tokens-a-trinity">Enviando Tokens a Trinity</h4>
<p>Ahora que hemos probado el token y está funcionando como se esperaba, podemos enviar algunos tokens a <code>Trinity</code>. Enviaremos 1000 tokens a <code>Trinity</code> usando la etiqueta <code>Action = "Transfer"</code>.</p>
<pre><code class="lang-sh">Send({ Target = ao.id, Action = <span class="hljs-string">"Transfer"</span>, Recipient = Trinity, Quantity = <span class="hljs-string">"1000"</span>})
</code></pre>
<p>Cuando <code>Trinity</code> reciba los tokens, responderá a la transferencia con un mensaje para confirmar que ha recibido los tokens.</p>
<p>Su respuesta se verá algo así:</p>
<p><code>Trinity:</code> "Token recibido. Interesante. No estaba segura de que llegarías tan lejos. Estoy impresionada, pero aún no hemos</p>
<p>terminado. Quiero que uses este token para tokengatear el chatroom. Haz eso, y entonces creeré que podrías ser el elegido."</p>
<p>Has completado el proceso de crear un token y enviarlo a <code>Trinity</code>. Ahora estás listo para avanzar al siguiente paso en el tutorial. Tokengateando el Chatroom.</p>
<hr />
<h2 id="heading-tokengateando-el-chatroom">Tokengateando el Chatroom</h2>
<p>::: info Ahora que hemos creado un token y lo hemos enviado a <code>Trinity</code>, podemos usar el token para tokengatear nuestro chatroom. Esto permitirá que solo aquellos que tengan el token puedan entrar al chatroom. :::</p>
<h3 id="heading-como-tokengatear-el-chatroom">Cómo Tokengatear el Chatroom</h3>
<p>Creemos un manejador que nos permita tokengatear el chatroom. Este manejador responderá a la etiqueta <code>Action = "Broadcast"</code>, lo que significa que reemplazará el manejador original <code>Broadcast</code> que construimos para nuestro chatroom.</p>
<h3 id="heading-paso-1-inicia-el-mismo-proceso-aos">Paso 1: Inicia el mismo proceso <code>AOS</code>.</h3>
<p>Asegúrate de estar utilizando el mismo proceso <code>AOS</code> que has usado a lo largo del tutorial.</p>
<h3 id="heading-paso-2-abre-el-archivo-chatroomlua">Paso 2: Abre el archivo <code>chatroom.lua</code>.</h3>
<p>Este es el mismo archivo que usamos para crear el chatroom durante el tutorial de <a target="_blank" href="chatroom">chatroom</a>.</p>
<h3 id="heading-paso-3-edita-tu-manejador-broadcast">Paso 3: Edita tu manejador <code>Broadcast</code>.</h3>
<p>Reemplaza el manejador <code>Broadcast</code> original con el siguiente código:</p>
<pre><code class="lang-lua">Handlers.add(
    "Broadcast",
    Handlers.utils.hasMatchingTag("Action", "Broadcast"),
    function(m)
        if Balances[m.From] == nil or tonumber(Balances[m.From]) &lt; 1 then
            print("UNAUTH REQ: " .. m.From)
            return
        end
        local type = m.Type or "Normal"
        print("Broadcasting message from " .. m.From .. ". Content: " .. m.Data)
        for i = 1, #Members, 1 do
            ao.send({
                Target = Members[i],
                Action = "Broadcasted",
                Broadcaster = m.From,
                Data = m.Data
            })
        end
    end
)
</code></pre>
<p>Este manejador ahora verificará el saldo del token del remitente antes de difundir el mensaje al chatroom. Si el remitente no tiene un token, el mensaje no será difundido.</p>
<p>Guarda el archivo.</p>
<h3 id="heading-paso-4-recarga-el-archivo-chatroomlua">Paso 4: Recarga el archivo <code>chatroom.lua</code>.</h3>
<p>Para reemplazar el manejador <code>broadcast</code> original con el nuevo, necesitarás recargar el archivo <code>chatroom.lua</code>.</p>
<pre><code class="lang-sh">.load chatroom.lua
</code></pre>
<h3 id="heading-paso-5-prueba-el-tokengate">Paso 5: Prueba el Tokengate</h3>
<p>Ahora que el chatroom está tokengateado, probémoslo enviando un mensaje al chatroom.</p>
<h4 id="heading-desde-el-proceso-aos-original">Desde el proceso aos original</h4>
<p>Primero, lo probaremos desde el proceso AOS original.</p>
<pre><code class="lang-sh">Send({ Target = ao.id , Action = <span class="hljs-string">"Broadcast"</span>, Data = <span class="hljs-string">"Hello"</span> })
<span class="hljs-comment"># Resultados esperados:</span>
message added to outbox
Broadcasting message from Neo. Content: Hello.
</code></pre>
<h3 id="heading-probando-desde-otro-id-de-proceso">Probando desde otro ID de Proceso.</h3>
<h4 id="heading-desde-un-nuevo-proceso-aos">Desde un nuevo proceso AOS</h4>
<p>Ahora, probémoslo desde un nuevo proceso AOS que no tenga un token.</p>
<pre><code class="lang-sh">aos chatroom-no-token <span class="hljs-comment"># `chatroom-no-token` es el nuevo nombre del proceso</span>
</code></pre>
<p>Primero necesitaremos registrarnos en el chatroom.</p>
<pre><code class="lang-sh">.load chatroom.lua
Send({ Target = [Tu ID de Proceso], Action = <span class="hljs-string">"Register"</span> })
<span class="hljs-comment"># Resultados esperados:</span>
message added to outbox
New Message From [Tu ID de Proceso]: Data = registered
</code></pre>
<p>Ahora, intentemos enviar un mensaje al chatroom.</p>
<pre><code class="lang-sh">Send({ Target = [Tu ID de Proceso] , Action = <span class="hljs-string">"Broadcast"</span>, Data = <span class="hljs-string">"Hello?"</span> })
<span class="hljs-comment"># Resultados esperados:</span>
message added to outbox
UNAUTH REQ: [Nuevo ID de Proceso]
</code></pre>
<p>Como puedes ver, el mensaje no fue difundido porque el nuevo proceso no tiene un token.</p>
<h3 id="heading-dile-a-trinity-se-ha-hecho">Dile a Trinity "Se ha hecho"</h3>
<p>Desde el proceso AOS original, envía un mensaje difundido al chatroom diciendo, "Se ha hecho".</p>
<pre><code class="lang-lua">Send({ Target = ao.id , Action = "Broadcast", Data = "Se ha hecho" })
</code></pre>
<p>::: warning Es importante ser consciente de la coincidencia exacta de datos y la sensibilidad a mayúsculas y minúsculas. Si no recibes una respuesta de Morpheus o Trinity, asegúrate de verificar el contenido de tus Datos y Etiquetas. :::</p>
<p>Trinity responderá entonces al chatroom siendo tokengateado.</p>
<h4 id="heading-resultados-esperados">Resultados Esperados:</h4>
<p>Trinity enviará un mensaje diciendo: "Supongo que Morpheus tenía razón. Eres el elegido. Considerame impresionada. Ahora estás listo para unirte al Construct, un chatroom exclusivo disponible solo para aquellos que han completado este tutorial. Ahora, únete a los demás usando la misma etiqueta que usaste `Register</p>
<p>`, con este ID de proceso: [ID de Proceso del Construct] Buena suerte. -Trinity". Además, seguirá un pie de página al mensaje.</p>
<p>::: warning Lee cuidadosamente el pie de página en el mensaje de Trinity para descubrir cómo presentar tu reclamo y recibir tu CRED. :::</p>
<h3 id="heading-conclusion-1">Conclusión</h3>
<p>¡Lo has hecho! Has tokengateado con éxito el chatroom. Esto ha desbloqueado ahora el acceso al <code>Construct</code>, donde solo aquellos que han completado completamente este tutorial pueden entrar.</p>
<h4 id="heading-felicidades">¡Felicidades!</h4>
<p>Has demostrado un gran potencial. Espero que hayas disfrutado este tutorial. Ahora estás listo para construir libremente en <code>AO</code>.</p>
<p>Si quieres aprender más sobre <code>AO + AOS</code>, entonces dirígete a la documentación en <a target="_blank" href="https://cookbook_ao.arweave.dev">https://cookbook_ao.arweave.dev</a>.</p>
<p>Hasta la próxima, nos vemos en la Máquina.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding POKT Network: A Comprehensive Guide to Decentralized RPC Services]]></title><description><![CDATA[There are several RPC (Remote Procedure Call) Providers to choose from: Alchemy, Infura, Ankr, Quicknode, POKT, and more.
So, why choose POKT?
What makes it different?
Let's dive into the many differences as to why you would choose POKT Network over ...]]></description><link>https://blog.patrickskinner.tech/understanding-pokt-network-a-comprehensive-guide-to-decentralized-rpc-services</link><guid isPermaLink="true">https://blog.patrickskinner.tech/understanding-pokt-network-a-comprehensive-guide-to-decentralized-rpc-services</guid><category><![CDATA[Blockchain]]></category><category><![CDATA[RPC]]></category><category><![CDATA[Web3]]></category><category><![CDATA[network]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Tue, 26 Dec 2023 12:34:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703591309739/68190ba5-f933-432e-82c1-b9ef0443815f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are several RPC (Remote Procedure Call) Providers to choose from: Alchemy, Infura, Ankr, Quicknode, POKT, and more.</p>
<p>So, why choose POKT?</p>
<p>What makes it different?</p>
<p>Let's dive into the many differences as to why you would choose POKT Network over other providers.</p>
<h2 id="heading-intro-to-pokt-network-amp-rpc-providers">Intro to POKT Network &amp; RPC Providers</h2>
<blockquote>
<p><strong>POKT Network is a decentralized infrastructure that acts as a middleman between blockchain data and the applications that need it.</strong></p>
</blockquote>
<p>Imagine it like a bridge:</p>
<ul>
<li><p><strong>On one side,</strong> you have various blockchains overflowing with valuable information, like transaction history, smart contract code, and token balances.</p>
</li>
<li><p><strong>On the other side,</strong> you have developers building the future of Web3 with decentralized applications (dApps). These dApps rely on accessing specific data from multiple blockchains to function properly.</p>
</li>
</ul>
<p>However, there's a gap between these two worlds. Blockchains are complex and accessing their data directly can be challenging, especially for developers working with multiple chains.</p>
<p><strong>This is where POKT Network comes in.</strong> It builds the bridge with two key components:</p>
<ul>
<li><p><strong>Relay Network:</strong> Independent node providers run software ("relays") that connect to various blockchains. These relays act like toll booths, fetching and verifying data on demand.</p>
</li>
<li><p><strong>Decentralized Marketplace:</strong> Developers can access needed data by paying node providers with POKT, the network's native token. This creates a peer-to-peer marketplace for blockchain data, free from centralized control.</p>
</li>
</ul>
<h2 id="heading-benefits-of-using-an-rpc-provider">Benefits of using an RPC Provider</h2>
<p>There are many benefits to using an RPC provider, making them a popular choice for developers working with blockchains and other distributed systems. Here are some key advantages:</p>
<p><strong>Simplifies Development:</strong></p>
<ul>
<li><p><strong>Abstraction:</strong> RPC providers hide the complexities of network communication and remote procedure calls. Developers can focus on writing code to utilize the requested data or functionality without worrying about the underlying infrastructure.</p>
</li>
<li><p><strong>Standardized APIs:</strong> Most providers offer well-documented APIs that simplify interacting with various blockchains or services. This saves developers time and effort compared to building their own communication protocols.</p>
</li>
<li><p><strong>Reduced Codebase:</strong> Utilizing RPC calls promotes code reuse and avoids the need to develop and maintain individual node infrastructure, keeping your codebase cleaner and more manageable.</p>
</li>
</ul>
<p><strong>Improved Performance and Scalability:</strong></p>
<ul>
<li><p><strong>High Availability:</strong> RPC providers often have geographically distributed infrastructure, ensuring high uptime and redundancy compared to running your own node. This translates to reliable access to data and services even during outages or high traffic.</p>
</li>
<li><p><strong>Faster Response Times:</strong> Many providers invest in optimizing their infrastructure for low latency and fast response times, crucial for real-time applications and performance-sensitive interactions.</p>
</li>
<li><p><strong>Scalability:</strong> Providers can readily adapt to increased demand by scaling their infrastructure dynamically, eliminating the need for developers to manage node scaling themselves.</p>
</li>
</ul>
<p><strong>Cost-Effectiveness:</strong></p>
<ul>
<li><p><strong>Pay-as-you-go:</strong> Most providers offer flexible pricing plans, allowing developers to pay only for the resources they use. This can be significantly cheaper than running and maintaining your own nodes, especially for projects with fluctuating data needs.</p>
</li>
<li><p><strong>Reduced Hardware Requirements:</strong> Using an RPC provider eliminates the need for dedicated hardware to run your own nodes, saving on upfront costs and ongoing maintenance.</p>
</li>
</ul>
<p><strong>Additional Benefits:</strong></p>
<ul>
<li><p><strong>Security:</strong> Many providers offer advanced security features like DDoS protection and intrusion detection, enhancing the security of your application's interactions with the blockchain.</p>
</li>
<li><p><strong>Feature-rich Services:</strong> Some providers offer additional features like analytics, transaction history monitoring, and node management tools, providing valuable insights and simplifying development processes.</p>
</li>
<li><p><strong>Community and Support:</strong> Many providers have active communities and dedicated support teams, offering assistance and troubleshooting resources for developers encountering issues.</p>
</li>
</ul>
<p>However, it's important to remember that some potential drawbacks also exist:</p>
<ul>
<li><p><strong>Centralization Concerns:</strong> Using a centralized RPC provider introduces a potential single point of failure and raises concerns about censorship. Decentralized RPC providers like POKT aim to address these concerns.</p>
</li>
<li><p><strong>Vendor Lock-in:</strong> Relying on a single provider can create vendor lock-in, making it difficult to switch providers later. Choosing a provider with robust APIs and open standards can mitigate this risk.</p>
</li>
<li><p><strong>Security Risks:</strong> Choosing a reputable provider with strong security practices is crucial to ensure the safety of your applications and data.</p>
</li>
</ul>
<p>Overall, the benefits of using an RPC provider often outweigh the drawbacks for most developers. Weighing your specific needs and priorities against the available providers will help you choose the best option for your project.</p>
<h2 id="heading-what-sets-pokt-network-apart-from-other-rpc-providers">What Sets POKT Network Apart From Other RPC Providers?</h2>
<h3 id="heading-the-rpc-trilemma">The RPC Trilemma</h3>
<p><a target="_blank" href="https://pokt.network"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703593562215/18a95e8f-55d3-414a-aea4-c3d12c9f5ff4.png" alt class="image--center mx-auto" /></a></p>
<p>The <strong>RPC Trilemma</strong> is a fundamental challenge faced by developers building applications that rely on blockchain data. It posits that achieving <strong>all three</strong> of the following goals is near-impossible:</p>
<ul>
<li><p><strong>Performance:</strong> Fast response times and high throughput for data requests.</p>
</li>
<li><p><strong>Reliability:</strong> Consistent availability and uptime of the data source.</p>
</li>
<li><p><strong>Cost:</strong> Affordable, scalable pricing for data access.</p>
</li>
</ul>
<p>Traditionally, developers have faced trade-offs when addressing these goals. Centralized RPC providers might offer better performance and reliability, but often at the expense of higher costs and potential censorship concerns. Conversely, decentralized alternatives can be more cost-effective and censorship-resistant, but sometimes struggle with achieving consistent performance and uptime.</p>
<p>This is where <strong>POKT Network</strong> comes in, claiming to <strong>navigate the RPC Trilemma</strong> through its unique, community-driven approach:</p>
<ul>
<li><p><strong>Performance:</strong> POKT leverages a network of independent node providers ("relays") distributed across the globe, aiming to reduce latency and increase data accessibility. The competition between relays incentivizes them to optimize their infrastructure for faster response times.</p>
</li>
<li><p><a target="_blank" href="https://pokt.network"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703591466649/365a0393-f118-4126-a545-3182228ff76d.png" alt class="image--center mx-auto" /></a></p>
<p>  <strong>Reliability:</strong> The P2P network architecture provides redundancy and resilience. If one node goes down, others can pick up the slack, minimizing downtime and ensuring consistent data availability.</p>
</li>
<li><p><strong>Cost:</strong> POKT utilizes a token-based payment system, allowing developers to pay only for the data they consume. This eliminates upfront fees and scales naturally with usage, promoting affordability.</p>
</li>
</ul>
<p><strong>Additionally, POKT addresses the Trilemma through:</strong></p>
<ul>
<li><p><strong>Community-driven governance:</strong> Token holders participate in network upgrades and feature development, ensuring the platform evolves to meet user needs and optimize performance, reliability, and cost.</p>
</li>
<li><p><strong>Open-source code:</strong> Transparency fosters trust and allows developers to contribute to the network's optimization and improvement.</p>
</li>
<li><p><a target="_blank" href="https://pokt.network"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703591524454/e94e3741-9082-4a0e-ba63-c9de51bd76dd.png" alt class="image--center mx-auto" /></a></p>
<p>  <strong>Multi-chain compatible:</strong> POKT is actively integrating other blockchains to cater to developers building cross-chain dApps, currently boasting support for 40+ different blockchains including: Ethereum, Polygon, Arbitrum, Binance Smart Chain, Optimism, Solana, Starknet, and many more. Check out the POKT's supported chains list here: <a target="_blank" href="https://docs.pokt.network/supported-chains">https://docs.pokt.network/supported-chains</a></p>
</li>
</ul>
<blockquote>
<h3 id="heading-pokt-supports-rpc-services-for-40-different-blockchains-including-ethereum-polygon-arbitrum-binance-smart-chain-optimism-solana-starknet-and-many-more">POKT supports RPC services for 40+ different blockchains, including: Ethereum, Polygon, Arbitrum, Binance Smart Chain, Optimism, Solana, Starknet, and many more...</h3>
</blockquote>
<ul>
<li><strong>Customization options:</strong> Developers can choose specific node providers and configure their requests for optimal performance and cost efficiency.</li>
</ul>
<h2 id="heading-does-pokt-network-provide-free-rpc-services">Does POKT Network Provide Free RPC Services?</h2>
<blockquote>
<h3 id="heading-yes-with-the-ability-to-create-two-apps-and-mint-an-unlimited-number-of-endpoints-for-free-no-credit-card-required">Yes, with the ability to create two apps and mint an unlimited number of endpoints for free. No Credit Card Required.</h3>
</blockquote>
<p>Here's how it works:</p>
<p><strong>Free Tier Features:</strong></p>
<ul>
<li><p><strong>Create up to two applications:</strong> Spin up separate projects and experiment with different data needs.</p>
</li>
<li><p><strong>Mint unlimited endpoints per application:</strong> Craft as many data access points as your applications demand within the free tier.</p>
</li>
<li><p><strong>250,000 free relays per day:</strong> That's a substantial amount of data requests to play around with and build basic functionalities.</p>
</li>
<li><p><strong>No credit card required:</strong> Jump right in and start exploring without any upfront commitment.</p>
</li>
</ul>
<p><strong>Beyond the Free Tier:</strong></p>
<p>Once you've outgrown the free tier's limits, POKT's <strong>Pay-as-you-go (PAYG)</strong> system seamlessly takes over. You'll only pay for the data requests your applications actually generate, ensuring cost-effective scaling for your growing needs.</p>
<p><strong>Here's what PAYG offers:</strong></p>
<ul>
<li><p><strong>Transparent billing:</strong> Pay a tiny fraction of a cent per additional relay beyond the free tier.</p>
</li>
<li><p><strong>Monthly invoices:</strong> Keep track of your data usage and costs clearly.</p>
</li>
<li><p><strong>No overage charges:</strong> Never worry about unexpected bills, thanks to the predictable PAYG model.</p>
</li>
</ul>
<p><strong>Whether you're a seasoned developer or a curious newcomer, POKT Network's free tier and flexible PAYG system make exploring blockchain data accessible and affordable.</strong> Dive in, experiment, and unlock the possibilities of a decentralized data future!</p>
<p><strong>Additional Resources:</strong></p>
<ul>
<li><p><strong>POKT Network Documentation:</strong> <a target="_blank" href="https://docs.pokt.network/get-rpcs/public-endpoints">https://docs.pokt.network/get-rpcs/public-endpoints</a></p>
</li>
<li><p><strong>POKT Network Blog:</strong> <a target="_blank" href="https://docs.pokt.network/access-tutorials/operate-a-node">https://www.pokt.network/blog/pay-as-you-go-explained</a></p>
</li>
</ul>
<h2 id="heading-wrapping-things-up">Wrapping Things Up:</h2>
<p><strong>For aspiring dApp architects, POKT Network is an open invitation to the future of web3.</strong> Its decentralized marketplace for blockchain data fosters <strong>freedom and empowerment</strong>, offering a refreshing alternative to centralized, siloed models. Start building without breaking the bank with a <strong>generous free tier</strong> and a <strong>transparent pay-as-you-go system</strong>, scaling seamlessly as your dApp takes flight. <strong>Unleash your creativity with unlimited endpoint creation</strong>, crafting tailored data pipelines for your unique vision. Embrace <strong>community-driven governance</strong> and shape the future of a <strong>censorship-resistant data landscape</strong>. Join the <strong>vibrant POKT Network ecosystem</strong> and build dApps that truly break the chains, all powered by the <strong>transparency, cost-effectiveness, and security</strong> of a thriving decentralized infrastructure. POKT isn't just a data provider, it's a <strong>passport to a more open, accessible, and equitable web3 world.</strong></p>
<p>Start building today and become a pioneer in the data revolution!</p>
<hr />
<p>For more detailed information on POKT Network, visit their <a target="_blank" href="https://www.pokt.network/">official website</a> and the <a target="_blank" href="https://docs.pokt.network/">POKT Network Documentation</a>.</p>
]]></content:encoded></item><item><title><![CDATA[EIP-4844: The Electric Car Solution to our EVM Gas Problem.]]></title><description><![CDATA[EIP-4844, also known as "Proto-Danksharding," is a significant upgrade proposal for the Ethereum network aiming to address two major challenges: high gas fees and scalability limitations.
The Problem:
Currently, Ethereum utilizes a single blockchain ...]]></description><link>https://blog.patrickskinner.tech/eip-4844</link><guid isPermaLink="true">https://blog.patrickskinner.tech/eip-4844</guid><category><![CDATA[Ethereum]]></category><category><![CDATA[EVM]]></category><category><![CDATA[Eip]]></category><category><![CDATA[eip4844]]></category><category><![CDATA[Gas fee]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Sun, 10 Dec 2023 13:45:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1702215038357/77feb710-01bf-40cd-a1d2-cad591dc036e.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>EIP-4844, also known as "Proto-Danksharding," is a significant upgrade proposal for the Ethereum network aiming to address two major challenges: <strong>high gas fees</strong> and <strong>scalability limitations</strong>.</p>
<p><strong>The Problem:</strong></p>
<p>Currently, Ethereum utilizes a <strong>single blockchain</strong> to store all transaction data, leading to congestion and high gas fees during periods of high activity. This bottleneck hinders user experience and limits the network's potential for widespread adoption.</p>
<p><strong>EIP-4844's Solution:</strong></p>
<p>This upgrade introduces three key innovations to tackle these issues:</p>
<ul>
<li><p><strong>Blob-Carrying Transactions:</strong></p>
<ul>
<li><p>Introduces a new transaction type capable of attaching large data segments ("blobs") without incurring exorbitant fees.</p>
</li>
<li><p>This enables efficient storage of bulky data, particularly for rollups, significantly reducing their overall gas costs.</p>
</li>
</ul>
</li>
<li><p><strong>Data Availability Sampling:</strong></p>
<ul>
<li><p>Implements a mechanism for verifying the existence of data within blobs without downloading the entire data chunk.</p>
</li>
<li><p>This ensures data integrity while minimizing storage requirements and gas consumption.</p>
</li>
</ul>
</li>
<li><p><strong>Delayed Data Removal:</strong></p>
<ul>
<li><p>Allows storing blobs on the Ethereum consensus layer for a predetermined period before automatic deletion.</p>
</li>
<li><p>This guarantees data accessibility for verification purposes while maintaining a manageable blockchain size.</p>
</li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702215177682/d11413eb-429a-499c-8128-647d4334e67c.png" alt class="image--center mx-auto" /></p>
<p><strong>Technical Implications:</strong></p>
<p>EIP-4844 introduces several technical changes to the Ethereum protocol:</p>
<ul>
<li><p><strong>State Rent:</strong> Introduces a mechanism called "state rent" to incentivize efficient data usage by charging users for storing data on the blockchain.</p>
</li>
<li><p><strong>Data Availability Checks:</strong> Implements cryptographic techniques for verifying the availability of data within blobs without requiring full downloads.</p>
</li>
<li><p><strong>Off-Chain Data Availability:</strong> Enables the use of off-chain data storage solutions, further reducing on-chain storage demands.</p>
</li>
</ul>
<p><strong>Impact and Benefits:</strong></p>
<p>The successful implementation of EIP-4844 is expected to yield significant benefits for the Ethereum ecosystem:</p>
<ul>
<li><p><strong>Reduced Gas Fees:</strong> By offloading bulky data to blobs, users will experience drastically lower transaction fees, especially for rollups.</p>
</li>
<li><p><strong>Increased Scalability:</strong> By optimizing data storage and processing, the network will be able to process more transactions per second, leading to improved scalability.</p>
</li>
<li><p><strong>Enhanced Rollup Performance:</strong> Rollups, a Layer 2 scaling solution, will benefit greatly from reduced gas fees and improved data handling capabilities.</p>
</li>
<li><p><strong>Sustainable Growth:</strong> EIP-4844 paves the way for future scaling solutions, ensuring the Ethereum network can accommodate future growth and adoption.</p>
</li>
</ul>
<p><strong>Current Status and Future:</strong></p>
<p>EIP-4844 is currently under active development and testing on several public and private testnets. There's active testing on Goerli, Sepolia, and Rinkeby. The final specifications are being finalized, and a mainnet launch is anticipated in late 2023 or early 2024.</p>
<p><strong>Conclusion:</strong></p>
<p>EIP-4844 represents a significant step towards a more efficient, scalable, and cost-effective Ethereum network. Its implementation has the potential to revolutionize the way users interact with the network and unlock new possibilities for the Ethereum ecosystem.</p>
<p><strong>Stay Informed:</strong></p>
<p>For further information and updates on EIP-4844, you can refer to the following resources:</p>
<ul>
<li><p><strong>Official website:</strong> <a target="_blank" href="https://www.eip4844.com/">https://www.eip4844.com/</a></p>
</li>
<li><p><strong>Ethereum Foundation blog:</strong> <a target="_blank" href="https://www.eip4844.com/">https://blog.ethereum.org/</a></p>
</li>
<li><p><strong>Testnet documentation:</strong> <a target="_blank" href="https://www.eip4844.com/">https://docs.prylabs.network/docs/getting-started</a></p>
</li>
<li><p><strong>Client updates:</strong> <a target="_blank" href="https://www.eip4844.com/">https://nimbus.guide/</a></p>
</li>
<li><p><strong>Community forums:</strong> <a target="_blank" href="https://www.eip4844.com/">https://ethereum-magicians.org/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Create a Static QR Code Generator using Vanilla JavaScript, HTML, and Tailwind CSS]]></title><description><![CDATA[Github Repo: https://github.com/PSkinnerTech/qrcode-vanilla/Demo: https://qrcode-vanilla.netlify.app/
In this tutorial, we will create a static QR Code Generator application using vanilla JavaScript, HTML, and Tailwind CSS. The application will allow...]]></description><link>https://blog.patrickskinner.tech/create-a-static-qr-code-generator-using-vanilla-javascript-html-and-tailwind-css</link><guid isPermaLink="true">https://blog.patrickskinner.tech/create-a-static-qr-code-generator-using-vanilla-javascript-html-and-tailwind-css</guid><category><![CDATA[Qrcode]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Mon, 17 Apr 2023 22:45:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681770600072/152bd2a1-e059-4e88-a5df-fe3c0400e36f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Github Repo:</strong> <a target="_blank" href="https://github.com/PSkinnerTech/qrcode-vanilla">https://github.com/PSkinnerTech/qrcode-vanilla/</a><br /><strong>Demo:</strong> <a target="_blank" href="https://qrcode-vanilla.netlify.app/">https://qrcode-vanilla.netlify.app/</a></p>
<p>In this tutorial, we will create a static QR Code Generator application using vanilla JavaScript, HTML, and Tailwind CSS. The application will allow users to enter a URL, generate a QR code, and download the QR code as an image.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before starting this tutorial, you should have a basic understanding of:</p>
<ul>
<li><p>HTML</p>
</li>
<li><p>CSS</p>
</li>
<li><p>JavaScript</p>
</li>
</ul>
<h2 id="heading-setting-up-the-project"><strong>Setting up the project</strong></h2>
<ol>
<li><p>Create a new folder called <code>qr-code-generator</code>.</p>
</li>
<li><p>Inside the <code>qr-code-generator</code> folder, create the following files and folders:</p>
</li>
</ol>
<pre><code class="lang-bash">codeqr-code-generator/
|-- img/
|   |-- qrcode.svg
|-- js/
|   |-- script.js
|-- index.html
</code></pre>
<h2 id="heading-creating-the-html-structure"><strong>Creating the HTML structure</strong></h2>
<p>Open the <code>index.html</code> file and add the following code:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span>
      <span class="hljs-attr">content</span>=<span class="hljs-string">"Generate a QR code image to point to a website URL"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">"keywords"</span>
      <span class="hljs-attr">content</span>=<span class="hljs-string">"qr code, qr codes, qrcodes, create qr code, qr code generator, create qrcodes, make a qr code, online qr code"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"shortcut icon"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/x-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.ico"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span> <span class="hljs-attr">crossorigin</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;700&amp;display=swap"</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tailwindcss.com"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      tailwind.config = {
        <span class="hljs-attr">theme</span>: {
          <span class="hljs-attr">extend</span>: {
            <span class="hljs-attr">fontFamily</span>: {
              <span class="hljs-attr">sans</span>: [<span class="hljs-string">"Poppins"</span>, <span class="hljs-string">"sans-serif"</span>],
            },
          },
        },
      };
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>
      <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"</span>
      <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA=="</span>
      <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>
      <span class="hljs-attr">referrerpolicy</span>=<span class="hljs-string">"no-referrer"</span>
    &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"js/script.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
      <span class="hljs-selector-id">#spinner</span> {
        <span class="hljs-attribute">display</span>: none;
      }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Free Online QR Code Generator<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 mb-10 bg-green-500"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-5xl m-auto"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl font-bold text-white"</span>&gt;</span>QR Code Generator<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"img/spinner.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col-reverse align-center justify-center m-auto md:max-w-4xl p-10 md:flex-row"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full md:w-2/3 mr-24"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-bold mb-5 md:text-4xl"</span>&gt;</span>QR Code Generator<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
            QR Codes allow smartphone users to access your website simply and
            quickly.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
            Enter your URL below to generate a QR Code and download the image.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"generate-form"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"url"</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"url"</span>
              <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter a URL"</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full border-2 border-gray-200 rounded p-3 text-grey-dark mr-2 focus:outline-none mb-5"</span>
              <span class="hljs-attr">required</span>
            /&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full border-2 border-gray-200 rounded p-3 text-grey-dark mr-2 focus:outline-none"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"size"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"size"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"100"</span>&gt;</span>100x100<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"200"</span>&gt;</span>200x200<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"300"</span> <span class="hljs-attr">selected</span>&gt;</span>300x300<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"400"</span>&gt;</span>400x400<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"500"</span>&gt;</span>500x500<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"600"</span>&gt;</span>600x600<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"700"</span>&gt;</span>700x700<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-600 rounded w-full text-white py-3 px-4 mt-5 hover:bg-black"</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            &gt;</span>
              Generate QR Code
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full md:w-1/3 self-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-1/2 m-auto mb-10 md:w-full"</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"img/qrcode.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"generated"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-5xl m-auto flex flex-col text-center align-center justify-center mt-20"</span>
      &gt;</span>
        <span class="hljs-comment">&lt;!-- Spinner --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"spinner"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"status"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"inline mr-2 w-24 h-24 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600"</span>
            <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 100 101"</span>
            <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
            <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
          &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
              <span class="hljs-attr">d</span>=<span class="hljs-string">"M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"</span>
              <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
            /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
              <span class="hljs-attr">d</span>=<span class="hljs-string">"M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"</span>
              <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentFill"</span>
            /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qrcode"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m-auto"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-styling-the-application-with-tailwind-css"><strong>Styling the application with Tailwind CSS</strong></h2>
<p>We have already included the Tailwind CSS CDN in the <code>&lt;head&gt;</code> section of our HTML file. We have also added a custom font, Poppins, from Google Fonts. The application should now be styled with Tailwind CSS.</p>
<h2 id="heading-adding-interactivity-with-javascript"><strong>Adding interactivity with JavaScript</strong></h2>
<p>We have already added the script tag for qrcode.js library and included our custom <code>script.js</code> file with the <code>defer</code> attribute in the <code>&lt;head&gt;</code> section. Open the <code>script.js</code> file and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> form = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"generate-form"</span>);
<span class="hljs-keyword">const</span> qr = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"qrcode"</span>);

<span class="hljs-comment">// Button submit</span>
<span class="hljs-keyword">const</span> onGenerateSubmit = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  e.preventDefault();

  clearUI();

  <span class="hljs-keyword">const</span> url = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"url"</span>).value;
  <span class="hljs-keyword">const</span> size = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"size"</span>).value;

  <span class="hljs-comment">// Validate url</span>
  <span class="hljs-keyword">if</span> (url === <span class="hljs-string">""</span>) {
    alert(<span class="hljs-string">"Please enter a URL"</span>);
  } <span class="hljs-keyword">else</span> {
    showSpinner();
    <span class="hljs-comment">// Show spinner for 1 sec</span>
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      hideSpinner();
      generateQRCode(url, size);

      <span class="hljs-comment">// Generate the save button after the qr code image src is ready</span>
      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Get save url</span>
        <span class="hljs-keyword">const</span> saveUrl = qr.querySelector(<span class="hljs-string">"img"</span>).src;
        <span class="hljs-comment">// Create save button</span>
        createSaveBtn(saveUrl);
      }, <span class="hljs-number">50</span>);
    }, <span class="hljs-number">1000</span>);
  }
};

<span class="hljs-comment">// Generate QR code</span>
<span class="hljs-keyword">const</span> generateQRCode = <span class="hljs-function">(<span class="hljs-params">url, size</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> qrcode = <span class="hljs-keyword">new</span> QRCode(<span class="hljs-string">"qrcode"</span>, {
    <span class="hljs-attr">text</span>: url,
    <span class="hljs-attr">width</span>: size,
    <span class="hljs-attr">height</span>: size,
  });
};

<span class="hljs-comment">// Clear QR code and save button</span>
<span class="hljs-keyword">const</span> clearUI = <span class="hljs-function">() =&gt;</span> {
  qr.innerHTML = <span class="hljs-string">""</span>;
  <span class="hljs-keyword">const</span> saveBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"save-link"</span>);
  <span class="hljs-keyword">if</span> (saveBtn) {
    saveBtn.remove();
  }
};

<span class="hljs-comment">// Show spinner</span>
<span class="hljs-keyword">const</span> showSpinner = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> spinner = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spinner"</span>);
  spinner.style.display = <span class="hljs-string">"block"</span>;
};

<span class="hljs-comment">// Hide spinner</span>
<span class="hljs-keyword">const</span> hideSpinner = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> spinner = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spinner"</span>);
  spinner.style.display = <span class="hljs-string">"none"</span>;
};

<span class="hljs-comment">// Create save button to download QR code as image</span>
<span class="hljs-keyword">const</span> createSaveBtn = <span class="hljs-function">(<span class="hljs-params">saveUrl</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> link = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"a"</span>);
  link.id = <span class="hljs-string">"save-link"</span>;
  link.className =
    <span class="hljs-string">"bg-green-500 hover:bg-black text-white font-bold py-2 rounded w-1/3 m-auto my-5"</span>;
  link.href = saveUrl;
  link.download = <span class="hljs-string">"qrcode"</span>;
  link.innerHTML = <span class="hljs-string">"Save Image"</span>;
  <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"generated"</span>).appendChild(link);
};

hideSpinner();

form.addEventListener(<span class="hljs-string">"submit"</span>, onGenerateSubmit);
</code></pre>
<p>This code handles the following functionality:</p>
<ul>
<li><p>Form submission</p>
</li>
<li><p>Generating the QR code</p>
</li>
<li><p>Displaying and hiding the spinner</p>
</li>
<li><p>Creating a save button to download the generated QR code</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we have built a static QR Code Generator using vanilla JavaScript, HTML, and Tailwind CSS. We have implemented form submission, QR code generation, and the ability to download the QR code as an image. This application provides a simple and efficient way for users to generate QR codes for URLs.</p>
<p>You can now use this QR Code Generator to create QR codes for your website or any other URLs you want to share quickly and easily. The application is easy to customize and extend, so feel free to make any adjustments or add new features to suit your needs.</p>
<p>If you enjoyed this tutorial, be sure to follow me on <a target="_blank" href="https://twitter.com/PSkinnerTech">Twitter</a> and subscribe to my blog!</p>
<p>Happy Building! (🧱 , 🚀 )</p>
]]></content:encoded></item><item><title><![CDATA[Buy Me a Coffee with Mailchain]]></title><description><![CDATA[Buy Me a Coffee using Mailchain - A Step-by-Step Tutorial
In this tutorial, you'll learn how to create a web application that allows users to buy you a coffee using Ethereum and send a thank you message using Mailchain. We'll be using React, Next.js,...]]></description><link>https://blog.patrickskinner.tech/buy-me-a-coffee-with-mailchain</link><guid isPermaLink="true">https://blog.patrickskinner.tech/buy-me-a-coffee-with-mailchain</guid><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Fri, 14 Apr 2023 20:00:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681756135281/0b0957e9-cef5-4244-b0d5-2c9ce42d886f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-buy-me-a-coffee-using-mailchain-a-step-by-step-tutorial"><strong>Buy Me a Coffee using Mailchain - A Step-by-Step Tutorial</strong></h1>
<p>In this tutorial, you'll learn how to create a web application that allows users to buy you a coffee using Ethereum and send a thank you message using Mailchain. We'll be using React, Next.js, and the Mailchain SDK.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<ul>
<li><p>Basic knowledge of JavaScript, React, and Next.js</p>
</li>
<li><p>Node.js and npm installed on your system</p>
</li>
<li><p>MetaMask browser extension installed and set up</p>
</li>
</ul>
<h2 id="heading-setting-up-the-project"><strong>Setting up the project</strong></h2>
<ol>
<li>Create a new Next.js project using the following command:</li>
</ol>
<pre><code class="lang-bash">vbnetCopy codenpx create-next-app buy-me-a-coffee
<span class="hljs-built_in">cd</span> buy-me-a-coffee
</code></pre>
<ol>
<li>Install the required dependencies:</li>
</ol>
<pre><code class="lang-bash">perlCopy codenpm install ethers @mailchain/sdk next dotenv
</code></pre>
<h2 id="heading-creating-the-smart-contract"><strong>Creating the Smart Contract</strong></h2>
<p>We'll create a simple Ethereum smart contract that allows users to buy a coffee and store a memo on the blockchain.</p>
<ol>
<li>Install the required development dependencies:</li>
</ol>
<pre><code class="lang-bash">bashCopy codenpm install -D hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
</code></pre>
<ol>
<li>Initialize the Hardhat project:</li>
</ol>
<pre><code class="lang-bash">Copy codenpx hardhat
</code></pre>
<ol>
<li>Create a <code>contracts</code> directory and a <code>BuyMeACoffee.sol</code> file inside it:</li>
</ol>
<pre><code class="lang-bash">bashCopy codemkdir contracts
<span class="hljs-built_in">cd</span> contracts
touch BuyMeACoffee.sol
</code></pre>
<ol>
<li>Add the following smart contract code to <code>BuyMeACoffee.sol</code>:</li>
</ol>
<pre><code class="lang-bash">solidityCopy codepragma solidity ^0.8.0;

contract BuyMeACoffee {
    event NewMemo(
        address indexed from,
        uint256 timestamp,
        string name,
        string message
    );

    <span class="hljs-keyword">function</span> buyCoffee(string memory name, string memory message)
        public
        payable
    {
        require(msg.value &gt;= 0.001 ether, <span class="hljs-string">"Insufficient amount"</span>);
        emit NewMemo(msg.sender, block.timestamp, name, message);
    }
}
</code></pre>
<ol>
<li>Compile the smart contract:</li>
</ol>
<pre><code class="lang-bash">pythonCopy codenpx hardhat compile
</code></pre>
<ol>
<li>Deploy the smart contract to a local Ethereum network using Hardhat or to a testnet/mainnet using Remix or any other deployment tool. Note the deployed contract address.</li>
</ol>
<h2 id="heading-building-the-frontend"><strong>Building the Frontend</strong></h2>
<ol>
<li><p>Create a new file <code>utils/BuyMeACoffee.json</code> and store the smart contract ABI in it. You can find the ABI in the <code>artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json</code> file.</p>
</li>
<li><p>Create a new <code>pages/index.jsx</code> file and replace its content with the provided code in your initial question.</p>
</li>
<li><p>Update the <code>contractAddress</code> variable in <code>index.jsx</code> with the address of your deployed smart contract.</p>
</li>
<li><p>Modify the <code>index.jsx</code> file as suggested in the previous answers to conditionally render the ENS address input field and send the Mailchain message.</p>
</li>
</ol>
<h2 id="heading-configuring-mailchain"><strong>Configuring Mailchain</strong></h2>
<ol>
<li>Create a new file <code>mailchain.jsx</code> and add the following code:</li>
</ol>
<pre><code class="lang-bash">javascriptCopy codeimport { Mailchain } from <span class="hljs-string">"@mailchain/sdk"</span>;
import * as dotenv from <span class="hljs-string">"dotenv"</span>;
dotenv.config();

const secretRecoveryPhrase = process.env.SECRET_RECOVERY_PHRASE;

const mailchain = Mailchain.fromSecretRecoveryPhrase(secretRecoveryPhrase);
</code></pre>
<ol>
<li><p>Replace <code>SECRET_RECOVERY_PHRASE</code> with your own recovery phrase.</p>
</li>
<li><p>Create a <code>.env.local</code> file in the root of your project and add your recovery phrase:</p>
</li>
</ol>
<pre><code class="lang-bash">makefileCopy codeSECRET_RECOVERY_PHRASE=your_secret_recovery_phrase_here
</code></pre>
<h2 id="heading-running-the-application"><strong>Running the Application</strong></h2>
<ol>
<li>Start the development server:</li>
</ol>
<pre><code class="lang-bash">arduinoCopy codenpm run dev
</code></pre>
<ol>
<li>Open your browser and navigate to <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a>.</li>
</ol>
<h2 id="heading-using-the-application"><strong>Using the Application</strong></h2>
<ol>
<li><p>With the development server running, open your browser and navigate to <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a>.</p>
</li>
<li><p>Make sure your MetaMask extension is connected to the appropriate network (local, testnet, or mainnet) and has some Ether for transactions.</p>
</li>
<li><p>Click on the "Connect your wallet" button to connect your MetaMask wallet.</p>
</li>
<li><p>Once connected, you can enter your name, an optional message, and your ENS address (if you have one) in the provided input fields.</p>
</li>
<li><p>Click on the "Send 1 Coffee for 0.001ETH" button to send a transaction for buying a coffee.</p>
</li>
<li><p>MetaMask will prompt you to confirm the transaction. Confirm it and wait for the transaction to be mined.</p>
</li>
<li><p>Once the transaction is successful, the application will send a thank you message to the provided ENS address using Mailchain.</p>
</li>
<li><p>The recipient can then check their Mailchain inbox to see the thank you message.</p>
</li>
</ol>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Congratulations! You have successfully created a web application that allows users to buy you a coffee using Ethereum and send a thank you message using Mailchain. This tutorial demonstrated how to integrate Ethereum transactions and Mailchain messaging in a React application. You can further customize this application and add more features based on your requirements.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering the Art of DevRel with the Feynman Technique]]></title><description><![CDATA[Hey, what's up, everybody? Patrick Skinner here, and today we're diving into something that's going to change the way you approach your DevRel career – the Feynman Technique. In this article, we'll explore what the Feynman Technique is, how it works,...]]></description><link>https://blog.patrickskinner.tech/mastering-the-art-of-devrel-with-the-feynman-technique</link><guid isPermaLink="true">https://blog.patrickskinner.tech/mastering-the-art-of-devrel-with-the-feynman-technique</guid><category><![CDATA[DevRel]]></category><category><![CDATA[The Feynman Technique]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Sun, 09 Apr 2023 13:13:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681045655853/8d84db8a-bcf9-418e-a3b5-79c79eb324ce.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey, what's up, everybody? Patrick Skinner here, and today we're diving into something that's going to change the way you approach your DevRel career – <strong>the Feynman Technique</strong>. In this article, we'll explore what the Feynman Technique is, how it works, and how you can apply it to your DevRel practices. So, grab a cup of coffee, and let's get started!</p>
<p><img src="https://atomstalk.com/wp-content/uploads/2020/10/richard-feynman.jpg" alt="Richard Feynman: Journey of the Most Charismatic Physicist | AtomsTalk" /></p>
<h2 id="heading-what-is-the-feynman-technique"><strong>What is the Feynman Technique?</strong></h2>
<p>Named after the legendary physicist Richard Feynman, the Feynman Technique is a powerful method for learning and understanding complex concepts. It's all about breaking down ideas into their simplest forms and teaching them to others – a perfect fit for the DevRel world, where we're all about connecting with developers and sharing knowledge.</p>
<h2 id="heading-how-does-the-feynman-technique-work"><strong>How Does the Feynman Technique Work?</strong></h2>
<p>The Feynman Technique is built on four simple steps:</p>
<ol>
<li><p>Choose a concept: Pick a topic or concept that you want to learn and understand better.</p>
</li>
<li><p>Teach it to a child: Explain the concept in simple language, as if you were teaching it to a child or someone with no prior knowledge of the subject. This forces you to break down the idea into its most basic components and identify any gaps in your understanding.</p>
</li>
<li><p>Identify gaps and review: As you explain the concept, you may find areas where your understanding is incomplete or unclear. When you encounter these gaps, go back to your source material and review those sections to solidify your understanding.</p>
</li>
<li><p>Simplify and use analogies: Once you have a solid grasp on the concept, attempt to simplify your explanation further or use analogies to make it even more accessible.</p>
</li>
</ol>
<h2 id="heading-how-does-the-feynman-technique-apply-to-the-devrel-position"><strong>How Does the Feynman Technique Apply to the DevRel Position?</strong></h2>
<p>As a DevRel professional, you're always engaging with developers, creating educational content, and sharing complex ideas. The Feynman Technique can help you:</p>
<ul>
<li><p>Simplify complex concepts for better communication</p>
</li>
<li><p>Identify and fill gaps in your understanding</p>
</li>
<li><p>Create more engaging and accessible educational content</p>
</li>
<li><p>Improve your empathy for the developer community</p>
</li>
</ul>
<p><img src="https://scontent-bog1-1.xx.fbcdn.net/v/t1.6435-9/134046369_888200375316821_638955473085029195_n.jpg?_nc_cat=109&amp;ccb=1-7&amp;_nc_sid=a26aad&amp;_nc_eui2=AeGYPCoryvKRNap-g2p9LMJ0Yd1k7t_LpBth3WTu38ukG390STecVpa2XsHgnzVYqFw&amp;_nc_ohc=4adS2PIoFewAX_WBKDp&amp;_nc_ht=scontent-bog1-1.xx&amp;oh=00_AfCsxoDboTvmaybgGolRaFri9pVmzvyRuJdCXYMNX0m5aA&amp;oe=645A2991" alt="Feynman Technnique Diagram | by Arihant Educare" /></p>
<h2 id="heading-step-by-step-tutorial-applying-the-feynman-technique-to-your-devrel-practices"><strong>Step-By-Step Tutorial: Applying the Feynman Technique to Your DevRel Practices</strong></h2>
<p>Alright, now let's walk through the process of applying the Feynman Technique to your DevRel work.</p>
<h3 id="heading-step-1-choose-a-concept"><strong>Step 1: Choose a concept</strong></h3>
<p>Select a topic related to your company's products or services that you want to learn and understand better. This could be a specific feature, a programming concept, or an integration with another technology.</p>
<h3 id="heading-step-2-teach-it-to-a-child"><strong>Step 2: Teach it to a child</strong></h3>
<p>Find a quiet space, grab a piece of paper or a whiteboard, and start explaining the concept as if you were teaching it to a child or someone with no prior knowledge. Keep it simple and avoid jargon. Use plain language and focus on clarity. If you can't explain it simply, that means you don't understand it well enough yet!</p>
<h3 id="heading-step-3-identify-gaps-and-review"><strong>Step 3: Identify gaps and review</strong></h3>
<p>As you're explaining the concept, you might come across areas where you're not entirely clear or confident. Jot down these gaps and revisit your source material to review those sections. This step is essential for ensuring you have a solid understanding of the concept.</p>
<h3 id="heading-step-4-simplify-and-use-analogies"><strong>Step 4: Simplify and use analogies</strong></h3>
<p>Now that you've filled in the gaps, try to simplify your explanation even further. Use analogies to make the concept more relatable and easier to grasp. For example, if you're explaining APIs, you might compare them to a restaurant menu, where developers can choose what data or functionality they want to access.</p>
<h3 id="heading-step-5-put-it-into-practice"><strong>Step 5: Put it into practice</strong></h3>
<p>Now that you've mastered the concept using the Feynman Technique, it's time to put your newfound knowledge into practice. Here are some ways to incorporate the Feynman Technique into your DevRel activities:</p>
<ul>
<li><p>Engage with the developer community: Use your simplified explanations and analogies when answering questions on forums, social media, or during events. Your clear and accessible communication will make it easier for developers to grasp the concepts and connect with your company's products or services.</p>
</li>
<li><p>Create educational content: Apply the Feynman Technique when writing blog posts, tutorials, or documentation. By focusing on simplicity and clarity, your content will be more engaging and helpful for your audience, regardless of their skill level.</p>
</li>
<li><p>Host workshops or webinars: Share your expertise with the developer community by organizing workshops or webinars. Use your refined communication skills to deliver clear and concise presentations that resonate with your audience.</p>
</li>
<li><p>Advocate for the developer community within your company: Use the insights you've gained from the Feynman Technique to better understand the challenges and needs of developers. Share this feedback with your product and engineering teams to help shape the development of new features and improvements.</p>
</li>
</ul>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>And there you have it, folks! By incorporating the Feynman Technique into your DevRel practices, you'll not only become a better communicator but also deepen your understanding of the technologies and concepts you advocate for. So, give it a shot, and let me know how it works for you in the comments below.</p>
<p>Remember, the key to mastering any skill is practice and persistence. Keep working on the Feynman Technique and applying it to your DevRel work, and you'll be amazed by the results.</p>
<p>That's it for today, my friends. Let me know what you think of the article, its topic, the writing style, or anything else on your mind. And, of course, be sure to subscribe for more awesome content. Until next time, happy learning!</p>
]]></content:encoded></item><item><title><![CDATA[Guardian's Epic 60-Hour Journey Post-Hackathon]]></title><description><![CDATA[These last 60 hours have been an absolute whirlwind. While still maintaining my regular work, I've managed to push the ball pretty far down the field with Guardian. At this point, we've:

Written a whitepaper

Written a grant proposal

Built a full 5...]]></description><link>https://blog.patrickskinner.tech/guardians-epic-60-hour-journey-post-hackathon</link><guid isPermaLink="true">https://blog.patrickskinner.tech/guardians-epic-60-hour-journey-post-hackathon</guid><category><![CDATA[iot]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[whitepaper]]></category><category><![CDATA[Learning Journey]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Sat, 08 Apr 2023 04:05:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680926614729/ef47ff97-b339-4237-80bf-d079a8620806.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>These last 60 hours have been an absolute whirlwind. While still maintaining my regular work, I've managed to push the ball pretty far down the field with Guardian. At this point, we've:</p>
<ul>
<li><p>Written a <a target="_blank" href="https://v2.akord.com/public/vaults/active/M2hZy25EoD133W9PeNYsI3x_MwllS_uZy7l0Xbxmdjc/gallery#public/95a34f1a-1286-4370-8974-c8d14eeef793">whitepaper</a></p>
</li>
<li><p>Written a grant proposal</p>
</li>
<li><p>Built a full 5-page <a target="_blank" href="https://guardian-site.vercel.app/">website</a></p>
</li>
<li><p>Shot our Founders <a target="_blank" href="https://www.loom.com/share/61c923e1f7654804bceae77a28355cfc">intro video</a></p>
</li>
<li><p>And, finally, submitted a <a target="_blank" href="https://ycombinator.com">Y Combinator</a> application with only 10 mins to spare! 🥵</p>
</li>
</ul>
<p>Now, we just need a proper domain for the website. 🤔</p>
<p>The 🐣 Easter Weekend 🐣 is now in full swing, and things will need to slow down for me for a bit. I'm going to take the win of barely making it into Y Combinator's cutoff time as a clear signal to sit back, be present with the family, and strategize as to what to do next.</p>
<h2 id="heading-reflections">Reflections</h2>
<p>As I'm building very much out in the open, I can't believe the immense amount of support I've received from the community of developers around me, especially from <a target="_blank" href="https://developerdao.com">Developer DAO</a>. As a member of Developer DAO for almost a year now, I really can't imagine where I would be in my Developer Career. I would have never been encouraged to create content, participate in hackathons, and honestly know my own worth as a developer.</p>
<h2 id="heading-guardians-current-status">Guardian's Current Status</h2>
<p>Our Repos:</p>
<p>Patrick's: <a target="_blank" href="https://github.com/PSkinnerTech/airguardian-node">https://github.com/PSkinnerTech/airguardian-node</a></p>
<p>Tony's: <a target="_blank" href="https://github.com/tonykipkemboi/Streamr-Mini-Hackathon">https://github.com/tonykipkemboi/Streamr-Mini-Hackathon</a></p>
<p>Guardian is still just a couple of GitHub repos (they're both open-source, by the way, if you want to check them out). We need a bit of money to buy all the necessary components to start building the first MVP, which brings us to our "to-do" list.</p>
<ol>
<li><p>Create a list of grants to apply to.</p>
</li>
<li><p>Add some design features to the whitepaper and grant proposal.</p>
</li>
<li><p>Keep working on the Decentralized Dashboard for collecting and presenting real-time data.</p>
</li>
<li><p>Apply to Startups.com, Techstars, and other prominent accelerator programs.</p>
</li>
</ol>
<p>I'm thinking this is the right direction. What do you guys think? 🤔 This seems to be the logical path forward from this point. Let me know in the comments below! Until then, I'll write to you guys again within the next 24 hours. Peace!</p>
]]></content:encoded></item><item><title><![CDATA[From Volcano Ashes to Victory: My 72-Hour Hackathon Journey Creating AirGuardian]]></title><description><![CDATA[Last week, I participated in a Hackathon for Streamr, hosted by Developer_DAO, and guess what? I won first place and the grand prize of $3000 USD! But let me tell you, it was one wild ride packed into just 72 hours.
After attending an introduction we...]]></description><link>https://blog.patrickskinner.tech/from-volcano-ashes-to-victory-my-72-hour-hackathon-journey-creating-airguardian</link><guid isPermaLink="true">https://blog.patrickskinner.tech/from-volcano-ashes-to-victory-my-72-hour-hackathon-journey-creating-airguardian</guid><category><![CDATA[hackathon]]></category><category><![CDATA[developerdao]]></category><category><![CDATA[streamr]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Thu, 06 Apr 2023 03:09:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680748401478/cabf8467-7777-41f5-be06-070bbd1e2574.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week, I participated in a Hackathon for <a target="_blank" href="https://bit.ly/streamr-network">Streamr</a>, hosted by <a target="_blank" href="https://bit.ly/developer-dao">Developer_DAO</a>, and guess what? I won first place and the grand prize of <a target="_blank" href="https://bit.ly/developer-dao-streamr-hackathon-winners"><strong>$3000 USD</strong></a>! But let me tell you, it was one wild ride packed into just 72 hours.</p>
<p>After attending an <a target="_blank" href="https://www.youtube.com/watch?v=ghkWto6L5Zc">introduction webinar</a> by <a target="_blank" href="https://twitter.com/nicoburkart">@nicoburkart</a>, DevRel for Streamr, I dove headfirst into their <a target="_blank" href="https://docs.streamr.network/">documentation</a>. I was instantly captivated by the speed of their data streams and their IoT integration capabilities. With my startup, <a target="_blank" href="https://bit.ly/agami-co">AGAMI</a>, I've gained valuable experience in building IoT devices, so the idea of creating a physical product that generates decentralized data felt like a thrilling challenge.</p>
<p>I brainstormed for a few hours, initially toying with the idea of a child kidnapping/trafficking device. But let's be real, that was way too ambitious for a 72-hour project. Then, inspiration struck! Living in Colombia, my family and I are not far from an active volcano. While the lava isn't a major concern, the poisonous ash is. Local news advised people to have evacuation plans, and that got me thinking: "What if there was an early warning sign for potentially harmful air quality?" And that's when the lightbulb went off – <a target="_blank" href="https://blog.patrickskinner.tech/the-airguardian-node-how-to-build-a-fully-decentralized-air-quality-monitoring-node">AirGuardian</a>, a decentralized air quality monitoring system, was born.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680749183203/368885c7-d864-4b87-a347-dfae80060bcb.png" alt class="image--center mx-auto" /></p>
<p>The support from the Developer DAO community was incredible; they loved the idea and encouraged me to build it. The project had two parts: the AirGuardian Nodes (<a target="_blank" href="https://github.com/PSkinnerTech/airguardian-node">github repo</a>) to collect and send data to Streamr's pipelines, and the AirGuardian Monitoring system (<a target="_blank" href="https://github.com/PSkinnerTech/streamr-dapp">github repo</a>), a map displaying the geolocation of Nodes and the air quality data they collect.</p>
<p>I tackled the Nodes first. Funny enough, my experience with Raspberry Pi's only started when my son was old enough to build robots and other STEM projects. That knowledge proved invaluable for this build, setting up the Pi to interact with various components and push data to Streamr. Nico from Streamr was a huge help in guiding me through this process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680749448663/1cc6e158-ba10-4b13-9437-58ca5bc00a1d.png" alt class="image--center mx-auto" /></p>
<p>The second part was a bit trickier. I ambitiously wanted to create a fully decentralized map using Mapbox and deploying it onto Arweave. But time was running out, and I had a ton of bugs to fix. So, I blew the Developer_DAO conch shell horn and yelled, "DEVELOPERS... ASSSSEEMMMMMBBBBBLLLLE!" <a target="_blank" href="https://twitter.com/pbillingsby">@pbillingsby</a> was the first to answer the call. @pbillingsby is a DevRel for Arweave, a fellow Developer_DAO member, and a level <strong>99 Debugging Grand Wizard</strong>. I learned so much from him in such a short period of time. But, despite his efforts to bestow me with his debugging wisdom, was out of time and opted for a basic map as an MVP. Still, no regrets. I learned so much for at least making the bold attempt.</p>
<iframe src="https://giphy.com/embed/cCalRsU3yKZoQILEEI" width="480" height="270" class="giphy-embed"></iframe>

<p>Submitting my project was nerve-wracking – my first-ever Hackathon! I sought feedback from Developer_DAO members who were amazingly constructive in their criticism. The support from the community was incredible, and I couldn't be more grateful. To my astonishment, I started receiving messages about my entry, even from companies I had previously applied to.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680749950586/782e54eb-2a93-4151-8b13-b3e03e5c4fb9.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680749895336/def3a254-4e16-4867-b7cb-d818945e888d.png" alt class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://twitter.com/developer_dao/status/1643629948454748163"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680749861663/3be31913-ba91-47db-9b34-ee2f741442fe.png" alt class="image--center mx-auto" /></a></p>
<p>It was during one of those interviews that I discovered I'd won the Hackathon! I couldn't contain my excitement and even did the "All the Single Ladies" dance to celebrate.</p>
<iframe src="https://giphy.com/embed/5Y3ecILCEZlZiXM0eH" width="480" height="270" class="giphy-embed"></iframe>

<p>The experience was beyond amazing, and I'm now a Developer_DAO supporter for life. Streamr is an incredible product, and I'll keep building on this project. The 2nd place winner, <a target="_blank" href="https://twitter.com/tonykipkemboi">@tonykipkemboi</a>, built a similar product for seismic activity detection, and we're joining forces to apply for grants and build create one super product. With me, Tony, Developer_DAO, Streamr and our powers combined.... 👀  </p>
<iframe src="https://giphy.com/embed/kQYNaEa35hQ6pCYywH" width="480" height="360" class="giphy-embed"></iframe>

<p>With Y Combinator and Techstars application deadlines just around the corner, I'm seizing the opportunity and applying. You never know what might happen when you take a shot – and this Hackathon experience was a perfect reminder of that.</p>
<iframe src="https://giphy.com/embed/GcSqyYa2aF8dy" width="480" height="358" class="giphy-embed"></iframe>]]></content:encoded></item><item><title><![CDATA[The AirGuardian Node:  How to Build a Fully Decentralized Air Quality Monitoring Node]]></title><description><![CDATA[Introduction
Air pollution is a growing concern across the globe, affecting the health and well-being of millions of people. As urbanization continues to increase and industrial activities expand, monitoring air quality has become a crucial task for ...]]></description><link>https://blog.patrickskinner.tech/the-airguardian-node-how-to-build-a-fully-decentralized-air-quality-monitoring-node</link><guid isPermaLink="true">https://blog.patrickskinner.tech/the-airguardian-node-how-to-build-a-fully-decentralized-air-quality-monitoring-node</guid><category><![CDATA[decentralization]]></category><category><![CDATA[Eco-friendly]]></category><category><![CDATA[air quality monitoring]]></category><category><![CDATA[streamr]]></category><category><![CDATA[Arweave]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Sat, 01 Apr 2023 11:27:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680521084738/7e560157-6403-4ace-b660-4698f9028664.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Air pollution is a growing concern across the globe, affecting the health and well-being of millions of people. As urbanization continues to increase and industrial activities expand, monitoring air quality has become a crucial task for governments, businesses, and individuals alike. To address this challenge, we present the AirGuardian Node, a decentralized air quality monitoring solution that empowers communities to take control of their environment.</p>
<p>The AirGuardian Node is a compact, cost-effective, and easy-to-build device that combines the power of a Raspberry Pi 4 with the precision of the SDS011 air quality sensor. By leveraging the capabilities of the Streamr Network, the AirGuardian Node allows users to collect, store, and share real-time air quality data from various locations. This information can be utilized to identify pollution hotspots, evaluate the effectiveness of emission reduction strategies, and raise awareness about the importance of clean air.</p>
<p>Designed with sustainability in mind, the AirGuardian Node is powered by solar energy, making it an environmentally friendly and self-sufficient solution for air quality monitoring. Its weatherproof enclosure and durable components ensure reliable operation in various outdoor settings, from bustling city streets to remote rural areas.</p>
<p>In this tutorial, we will guide you through the process of building, programming, and deploying your very own AirGuardian Node. We will cover everything from assembling the hardware and setting up the Raspberry Pi to connecting the air quality sensor and configuring the Streamr client for data streaming. By the end of this tutorial, you will have a fully functional AirGuardian Node that you can use to monitor and contribute to the collective understanding of air quality in your community.</p>
<h2 id="heading-use-cases">Use Cases:</h2>
<p>Here are several use cases for a decentralized air quality sensor using a Raspberry Pi 4 and an SDS011 sensor:</p>
<ol>
<li><p><strong>Urban air quality monitoring</strong>: Deploying multiple sensors across a city can help monitor air quality in real-time and provide valuable data for policymakers to implement strategies to improve air quality.</p>
</li>
<li><p><strong>Indoor air quality monitoring</strong>: The device can be used to monitor air quality inside homes, offices, schools, and other buildings to ensure healthy living and working conditions.</p>
</li>
<li><p><strong>Industrial emission monitoring</strong>: The device can be installed near industrial facilities to measure emissions from factories and plants, helping authorities enforce regulations and businesses to reduce their environmental impact.</p>
</li>
<li><p><strong>Traffic-related air pollution</strong>: The sensors can be placed near busy roads, intersections, and highways to monitor the impact of vehicular emissions on air quality.</p>
</li>
<li><p><strong>Agricultural air quality monitoring</strong>: Farmers can use the device to monitor air quality around their farms, detecting the presence of harmful chemicals or particles caused by pesticide use, fertilizers, or other agricultural processes.</p>
</li>
<li><p><strong>Construction site monitoring</strong>: The sensor can be installed at construction sites to measure dust, particulate matter, and other pollutants released during construction activities, helping to ensure compliance with environmental regulations and protect workers' health.</p>
</li>
<li><p><strong>Forest fire and wildfire detection</strong>: Deploying the devices in forested areas can help detect early signs of wildfires by monitoring changes in air quality, enabling rapid response to minimize damage.</p>
</li>
<li><p><strong>Air quality-based alert systems</strong>: The device can be integrated into alert systems that notify users when air quality reaches unhealthy levels, advising people with respiratory issues or other sensitivities to stay indoors or take other precautions.</p>
</li>
<li><p><strong>Smart city applications</strong>: The decentralized air quality sensors can be part of a larger smart city infrastructure that uses the data collected to optimize traffic flow, manage waste collection, and inform urban planning.</p>
</li>
<li><p><strong>Research and education</strong>: The device can be used by researchers and students to study the effects of air pollution on health, the environment, and climate change, as well as develop innovative solutions to improve air quality.</p>
</li>
</ol>
<h2 id="heading-what-youll-need">What you'll need:</h2>
<p>To build the decentralized air quality sensor using a Raspberry Pi 4 and an SDS011 sensor, you will need the following items and tools:</p>
<h3 id="heading-items">Items:</h3>
<ol>
<li><p>Raspberry Pi 4 (with 2GB, 4GB, or 8GB RAM, depending on your requirements)</p>
</li>
<li><p>32 GB microSD card (Class 10 or higher recommended)</p>
</li>
<li><p>SDS011 air quality sensor</p>
</li>
<li><p>USB to UART adapter (compatible with the SDS011 sensor)</p>
</li>
<li><p>Raspberry Pi power supply (USB-C, 5V, 3A)</p>
</li>
<li><p>Solar panel (with sufficient wattage to power the Raspberry Pi 4)</p>
</li>
<li><p>Solar charge controller (compatible with the solar panel and battery)</p>
</li>
<li><p>Battery (for storing solar energy and powering the Raspberry Pi during low sunlight)</p>
</li>
<li><p>Voltage converter or power supply (to convert the battery voltage to 5V for the Raspberry Pi)</p>
</li>
<li><p>Weatherproof enclosure (to protect the Raspberry Pi and SDS011 sensor from the elements)</p>
</li>
<li><p>Mounting hardware (to secure the device in the desired location)</p>
</li>
<li><p>Jumper wires and connectors (to connect the components)</p>
</li>
</ol>
<h3 id="heading-tools">Tools:</h3>
<ol>
<li><p>Computer (for programming and setting up the Raspberry Pi)</p>
</li>
<li><p>Raspberry Pi Imager (for installing the Raspberry Pi OS)</p>
</li>
<li><p>SSH client or terminal emulator (for remote access to the Raspberry Pi)</p>
</li>
<li><p>Soldering iron and solder (if required for making connections)</p>
</li>
<li><p>Wire strippers and cutters (for preparing jumper wires and connectors)</p>
</li>
<li><p>Screwdrivers (for assembling the enclosure and mounting hardware)</p>
</li>
<li><p>Multimeter (for testing connections and voltages)</p>
</li>
</ol>
<h3 id="heading-optional-items">Optional Items:</h3>
<ol>
<li><p>Wi-Fi dongle (if the Raspberry Pi 4's built-in Wi-Fi is not sufficient)</p>
</li>
<li><p>USB keyboard and HDMI monitor (for local access to the Raspberry Pi if not using SSH)</p>
</li>
<li><p>Heat sink and cooling fan (to keep the Raspberry Pi 4 cool during operation)</p>
</li>
<li><p>Real-time clock module (to maintain accurate time when the Raspberry Pi is not connected to the internet)</p>
</li>
<li><p>External antenna (to improve Wi-Fi or cellular connectivity, if required)</p>
</li>
</ol>
<h2 id="heading-tutorial">Tutorial</h2>
<p>Here's a comprehensive tutorial on how to code, build, and deploy a decentralized air quality sensor using a Raspberry Pi 4, an SDS011 sensor, and a 32 GB microSD card that pushes data to the Streamr network.</p>
<h3 id="heading-step-1-hardware-setup">Step 1: Hardware Setup</h3>
<p>1.1. Assemble the Raspberry Pi 4 with the microSD card. 1.2. Connect the SDS011 sensor to the Raspberry Pi using a USB to UART adapter.</p>
<h3 id="heading-step-2-software-setup">Step 2: Software Setup</h3>
<p>2.1. Install Raspberry Pi OS on the microSD card using the Raspberry Pi Imager tool or any other preferred method.</p>
<p>2.2. Set up Wi-Fi and SSH access on the Raspberry Pi (if not done during OS installation).</p>
<p>2.3. Connect to the Raspberry Pi using SSH or open the terminal on the Raspberry Pi.</p>
<h3 id="heading-step-3-install-required-libraries">Step 3: Install Required Libraries</h3>
<p>3.1. Update the package list and upgrade the installed packages:</p>
<pre><code class="lang-bash">codesudo apt update
sudo apt upgrade
</code></pre>
<h4 id="heading-32-install-nodejs-and-npm">3.2. Install Node.js and npm:</h4>
<pre><code class="lang-bash">codecurl -fsSL https://deb.nodesource.com/setup_16.x | sudo bash -
sudo apt install -y nodejs
</code></pre>
<h4 id="heading-33-verify-the-installation">3.3. Verify the installation:</h4>
<pre><code class="lang-bash">codenode -v
npm -v
</code></pre>
<h3 id="heading-step-4-create-the-project">Step 4: Create the Project</h3>
<h4 id="heading-41-create-a-new-directory-for-the-project-and-navigate-into-it">4.1. Create a new directory for the project and navigate into it:</h4>
<pre><code class="lang-bash">codemkdir air-quality-sensor
<span class="hljs-built_in">cd</span> air-quality-sensor
</code></pre>
<h4 id="heading-42-initialize-a-new-npm-project">4.2. Initialize a new npm project:</h4>
<pre><code class="lang-bash">codenpm init -y
</code></pre>
<h3 id="heading-step-5-install-dependencies">Step 5: Install Dependencies</h3>
<h4 id="heading-51-install-the-streamr-client-sds011-wrapper-and-dotenv-libraries">5.1. Install the <code>streamr-client</code>, <code>sds011-wrapper</code>, and <code>dotenv</code> libraries:</h4>
<pre><code class="lang-bash">codenpm install streamr-client sds011-wrapper dotenv
</code></pre>
<h3 id="heading-step-6-create-the-application">Step 6: Create the Application</h3>
<h4 id="heading-61-create-a-new-env-file-to-store-your-streamr-private-key">6.1. Create a new <code>.env</code> file to store your Streamr private key:</h4>
<pre><code class="lang-bash">codeecho <span class="hljs-string">"ETHEREUM_PrivateKey=YOUR_PRIVATE_KEY"</span> &gt; .env
</code></pre>
<p>Replace <code>YOUR_PRIVATE_KEY</code> with your actual private key.</p>
<h4 id="heading-62-create-a-new-indexjs-file-with-the-following-content">6.2. Create a new <code>index.js</code> file with the following content:</h4>
<pre><code class="lang-bash">codeconst StreamrClient = require(<span class="hljs-string">'streamr-client'</span>);
const SDS011Wrapper = require(<span class="hljs-string">'sds011-wrapper'</span>);
const fs = require(<span class="hljs-string">'fs'</span>);

require(<span class="hljs-string">'dotenv'</span>).config();

const client = new StreamrClient({
  auth: {
    privateKey: process.env.ETHEREUM_PrivateKey,
  },
});

const sensor = new SDS011Wrapper(<span class="hljs-string">'/dev/ttyUSB0'</span>); // Replace with the correct port <span class="hljs-keyword">for</span> the SDS011 sensor on your Raspberry Pi

sensor.setReportingMode(<span class="hljs-string">'query'</span>); // Set the SDS011 sensor to query mode
sensor.setWorkingPeriod(1); // Set the working period to 1 minute (optional)

const publishAirQualityData = async (pm25, pm10) =&gt; {
  const data = {
    pm25: pm25,
    pm10: pm10,
  };

  console.log(<span class="hljs-string">'Air Quality Data:'</span>, data);
  await client.publish(<span class="hljs-string">'STREAM-ID'</span>, data); // Replace STREAM-ID with your Stream ID
};

sensor.on(<span class="hljs-string">'measure'</span>, (pm25, pm10) =&gt; {
  publishAirQualityData(pm25, pm10);
});
</code></pre>
<p>Make sure to replace <code>/dev/ttyUSB0</code> with the correct port for the SDS011 sensor on your Raspberry Pi and <code>STREAM-ID</code> with your Stream ID.</p>
<h3 id="heading-step-7-run-the-application">Step 7: Run the Application</h3>
<h4 id="heading-71-run-the-indexjs-file">7.1. Run the <code>index.js</code> file:</h4>
<pre><code class="lang-bash">codenode index.js
</code></pre>
<p>The application will start collecting air quality data from the SDS011 sensor and publish it to the Streamr network.</p>
<h2 id="heading-in-closing">In Closing</h2>
<p>As we conclude this tutorial on building and deploying the AirGuardian Node, we hope that you have gained valuable insights and hands-on experience in creating a decentralized air quality monitoring system. By harnessing the power of cutting-edge technology and renewable energy, the AirGuardian Node offers an accessible, cost-effective, and sustainable solution for individuals and communities to monitor their environment.</p>
<p>By building and deploying your own AirGuardian Node, you are contributing to a global network of air quality sensors, providing crucial data that can inform decision-making, facilitate research, and raise public awareness about air pollution. As more people join this collective effort, we can create a comprehensive and accurate map of air quality around the world, empowering communities to take action and advocate for cleaner air.</p>
<p>We encourage you to share your experiences, challenges, and successes with the AirGuardian Node within your community and beyond. As a collaborative and open-source project, we believe that fostering a supportive and innovative community is essential for driving change and addressing air pollution challenges.</p>
<p>Thank you for embarking on this journey with us, and we look forward to witnessing the positive impact that the AirGuardian Node will have on our environment and our lives. Together, we can create a cleaner, healthier, and more sustainable future for all.</p>
]]></content:encoded></item><item><title><![CDATA[How to Create a "Badass Links Page" with 3D Motion Background Using Vanta.js]]></title><description><![CDATA[GitHub Repository: https://github.com/PSkinnerTech/badass-links-page
Demo Link: https://links.patrickskinner.tech

Required Knowledge For this Project:

HTML: Basic Understanding

CSS: Basic Understanding

Javascript: "I know what it is, but I've nev...]]></description><link>https://blog.patrickskinner.tech/how-to-create-a-badass-links-page-with-3d-motion-background-using-vantajs</link><guid isPermaLink="true">https://blog.patrickskinner.tech/how-to-create-a-badass-links-page-with-3d-motion-background-using-vantajs</guid><category><![CDATA[HTML5]]></category><category><![CDATA[CSS]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[vanta.js]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Tue, 21 Mar 2023 21:13:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679516744453/b92a1e62-5bda-4a80-8b7e-c9aad15ddaf6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>GitHub Repository:</strong> <a target="_blank" href="https://github.com/PSkinnerTech/badass-links-page">https://github.com/PSkinnerTech/badass-links-page</a></p>
<p><strong>Demo Link:</strong> https://links.patrickskinner.tech</p>
<p><img src="https://user-images.githubusercontent.com/78289253/226677973-b129c128-18f5-404c-965b-c56d9249fe59.gif" alt="badass-links-page" class="image--center mx-auto" /></p>
<h3 id="heading-required-knowledge-for-this-project">Required Knowledge For this Project:</h3>
<ul>
<li><p><strong>HTML</strong>: Basic Understanding</p>
</li>
<li><p><strong>CSS</strong>: Basic Understanding</p>
</li>
<li><p><strong>Javascript</strong>: "I know what it is, but I've never used it before."</p>
</li>
</ul>
<h3 id="heading-intro">Intro:</h3>
<p>This is a comprehensive tutorial on how to create a "Link in Bio" / "Linktree Clone" / "Badass Links Page". The only requirement that will not be discussed in this in this tutorial is to have a text editor. I personally use <a target="_blank" href="https://code.visualstudio.com/">vsCode</a>, but any text editor will work. For you to follow along exactly with what is detailed, I recommend using vsCode if you don't currently have one picked out.</p>
<p>Without any further ado, let's get into the tutorial:</p>
<h1 id="heading-step-by-step-guide">Step-By-Step Guide</h1>
<h2 id="heading-step-1-create-your-work-environment">Step: 1 - Create your work environment</h2>
<ul>
<li><p>Create a folder that will be your working environment.</p>
</li>
<li><p>Open your folder in your preferred text editor.</p>
</li>
<li><p>Create the necessary files:</p>
<ul>
<li><p>Index.html</p>
</li>
<li><p>style.css</p>
</li>
</ul>
</li>
</ul>
<p><em>Note: We're not going to use an image folder for the sole purpose of keeping this website as light as possible. We'll be using a decentralized file storage app called</em> <a target="_blank" href="https://akord.com/products/web-app"><em>AKORD</em></a> <em>to source all of our image.</em></p>
<h2 id="heading-step-2-create-your-html-wire-frame">Step 2: Create your HTML Wire Frame</h2>
<p>*Create your HTML wireframe starting with <code>&lt;!DOCTYPE html&gt;</code>.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-defining-tags">Defining Tags:</h3>
<ul>
<li><p><code>&lt;!DOCTYPE html&gt;</code> is an HTML (Hypertext Markup Language) declaration or document type declaration that specifies the version of HTML used in a web page. It is usually placed at the beginning of an HTML document and informs the web browser how to interpret the code that follows. It helps ensure that the web page is displayed correctly and consistently across different browsers and devices.</p>
</li>
<li><p><code>&lt;html&gt;</code> is an HTML element that serves as the root element of an HTML document. It is the container for all other HTML elements, and its opening and closing tags enclose all the content of the web page, including the head and body sections. The HTML code inside the <code>&lt;html&gt;</code> element describes the structure and content of the web page and is interpreted by web browsers to display the web page to users.</p>
</li>
<li><p><code>&lt;head&gt;</code> is an HTML element that is used to contain the metadata of a web page. This metadata includes information such as the title of the web page, links to external stylesheets and scripts, and other information that helps web browsers properly render the content of the web page. The <code>&lt;head&gt;</code> element is not visible on the web page and is usually located between the <code>&lt;html&gt;</code> and <code>&lt;body&gt;</code> elements.</p>
</li>
<li><p><code>&lt;body&gt;</code> is an HTML element that represents the main content of a web page that is visible to users. It contains all the content that is displayed on the web page, such as text, images, videos, forms, and more. The <code>&lt;body&gt;</code> element is located between the opening and closing tags of the <code>&lt;html&gt;</code> element and is typically the largest section of an HTML document. It is the part of the web page that users interact with and is rendered by web browsers to display the content to users.</p>
</li>
<li><p><code>&lt;header&gt;</code> is an HTML element that is used to represent the introductory content or navigation links at the top of a web page. It is typically used to display the logo, site title, and primary navigation links of a website. The <code>&lt;header&gt;</code> element is usually located at the top of the <code>&lt;body&gt;</code> section of an HTML document, but it can also be used within other sections of the page. It is a semantic element that helps search engines and screen readers better understand the content and structure of the web page.</p>
</li>
<li><p><code>&lt;main&gt;</code> is an HTML element that is used to represent the main content of a web page. It contains the primary content that is unique to the web page and should not be repeated across multiple pages. The <code>&lt;main&gt;</code> element is typically used to enclose the main article, section, or other content areas of a web page. It is a semantic element that helps search engines and screen readers better understand the structure and purpose of the web page. The <code>&lt;main&gt;</code> element should only be used once per page and should not be nested within other semantic elements like <code>&lt;article&gt;</code> or <code>&lt;section&gt;</code>.</p>
</li>
<li><p><code>&lt;ul&gt;</code> is an HTML element that is used to create an unordered list of items. It stands for "unordered list" and is used to group together related items in no particular order. The items are usually represented as bullet points, but the appearance can be customized with CSS. Each item in the list is represented by an <code>&lt;li&gt;</code> (list item) element, which is nested within the <code>&lt;ul&gt;</code> element. The <code>&lt;ul&gt;</code> element can also be nested within other list elements, such as <code>&lt;ol&gt;</code> (ordered list), to create more complex list structures.</p>
</li>
<li><p><code>&lt;li&gt;</code> is an HTML element that is used to represent an item in a list. It stands for "list item" and is typically used within <code>&lt;ul&gt;</code> (unordered list) or <code>&lt;ol&gt;</code> (ordered list) elements to create a bulleted or numbered list of items. Each <code>&lt;li&gt;</code> element contains the content of the list item, such as text, images, or links. The <code>&lt;li&gt;</code> element can also be used within other contexts, such as navigation menus, where it represents a menu item.</p>
</li>
</ul>
<h2 id="heading-step-3-fill-in-your-tags">Step 3: Fill in your <code>&lt;head&gt;</code> tags:</h2>
<ul>
<li>Add these 4 separate lines to your <code>&lt;head&gt;</code> tags:</li>
</ul>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"'viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>[Insert Name Here]'s Badass Links Page<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
</code></pre>
<h3 id="heading-defining-your-tags-baseline">Defining your <code>&lt;head&gt;</code> tags baseline:</h3>
<ul>
<li><p><code>&lt;meta charset="utf-8" /&gt;</code> is a HTML tag that specifies the character encoding for the document. The charset attribute tells the browser what character encoding the document is using, which is important for displaying text correctly. The <code>utf-8</code> value is a widely-used character encoding that supports a wide range of characters and is recommended for use in modern web development.</p>
</li>
<li><p><code>&lt;meta name="'viewport" content="width=device-width, initial-scale=1" /&gt;</code> is a HTML tag that sets the viewport, which is the visible area of a web page, on mobile devices. The <code>width=device-width</code> value sets the width of the viewport to the width of the device's screen, while <code>initial-scale=1</code> sets the initial zoom level to 100%, which is typically what users expect when viewing a web page on their mobile device. This tag is important for ensuring that the layout and content of a web page are displayed properly on mobile devices with different screen sizes and resolutions.</p>
</li>
<li><p><code>&lt;link rel="stylesheet" href="style.css" /&gt;</code> is a HTML tag used to link an external CSS file to a web page. The rel attribute specifies the relationship between the current document and the linked resource, in this case, a stylesheet. The href attribute specifies the path to the CSS file that contains the styles to be applied to the web page. By using this tag, the styles defined in the linked CSS file can be applied to the HTML elements of the web page, allowing for better control over the visual appearance of the web page.</p>
</li>
</ul>
<h2 id="heading-step-4-begin-filling-your-tags">Step 4: Begin Filling your <code>&lt;body&gt;</code> tags</h2>
<ul>
<li>Inside the <code>&lt;body&gt;</code> -&gt; <code>&lt;main&gt;</code> -&gt; <code>&lt;header&gt;</code>, add: <code>&lt;h1&gt;Your Name&lt;/h1&gt;</code></li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Your Name<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
</code></pre>
<ul>
<li>Inside the <code>&lt;body&gt;</code> -&gt; <code>&lt;main&gt;</code> -&gt; <code>&lt;ul&gt;</code> -&gt; <code>&lt;li&gt;</code> tags add:</li>
</ul>
<pre><code class="lang-xml">      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Personal Website<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Youtube<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Twitter<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Linkedin<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>My Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Buy Me Coffee<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p><em>Note: You don't have to add all of these if you're building this for your use. Use whatever you deem necessary</em></p>
<h3 id="heading-new-tag-definitions">New Tag Definitions:</h3>
<ul>
<li><code>&lt;a&gt;</code> is a HTML tag used to create a hyperlink to another webpage or resource. The <code>a</code> stands for "anchor" and it is often used to create clickable links on web pages. The <code>href</code> attribute is used to specify the destination of the link, which could be a web page, a file, an email address, or any other resource on the web. When a user clicks on the link, they will be taken to the destination specified in the <code>href</code> attribute.</li>
</ul>
<h2 id="heading-step-6-importing-font-awesome-icons">Step 6: Importing Font-Awesome Icons</h2>
<ul>
<li><p>Go to this website: https://cdnjs.com/libraries/font-awesome</p>
<ul>
<li><p>Find the very first font-awesome library link that ends in <code>/css/all.min.css</code>, then click the <code>&lt;/&gt;</code> icon to copy the import syntax</p>
</li>
<li><p>Paste the import syntax in your <code>&lt;head&gt;</code> tags.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> 
        <span class="hljs-attr">name</span>=<span class="hljs-string">"'viewport"</span> 
        <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span> 
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/font-                                                awesome/6.3.0/css/all.min.css"</span>
      <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ=="</span>
      <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>
      <span class="hljs-attr">referrerpolicy</span>=<span class="hljs-string">"no-referrer"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>[Insert Name Here]'s Badass Links Page<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
</code></pre>
<p><strong>What this does is that it gives us access to all of the free Font-Awesome icons that we could ever want to use for our website.</strong></p>
<ul>
<li><p>Go to this website: https://fontawesome.com/search</p>
<ul>
<li><p>Click the "Free" button that has a lightning bolt icon.</p>
</li>
<li><p>Search for the icon you want to add to the first link.</p>
</li>
<li><p>By default, it will show you the HTML syntax. Click on the syntax once to copy it.</p>
</li>
<li><p>past the syntax inside of the <code>&lt;a&gt;</code> tags you want the icon to be associated with.</p>
</li>
</ul>
</li>
<li><p>Do this for every link you want an icon for.</p>
</li>
</ul>
<h3 id="heading-new-definitions">New Definitions</h3>
<ul>
<li><code>&lt;i&gt;</code> is an HTML tag used to format text in italics. The <code>i</code> stands for "italic" and it is used to indicate that the enclosed text should be displayed in a slanted or cursive font style. However, it is important to note that the use of the <code>&lt;i&gt;</code> tag has been deprecated in favor of using the <code>&lt;em&gt;</code> tag to indicate emphasis, as it provides better semantic meaning to the text.</li>
</ul>
<h2 id="heading-step-7-add-hyperlinks-to-each">STEP 7: Add Hyperlinks to Each <code>&lt;li&gt;</code></h2>
<p><strong>To have the links work, we need to add an</strong> <code>href</code> <strong>inside each</strong> <code>a</code> <strong>tag inside the</strong> <code>li</code> <strong>tag.</strong></p>
<ul>
<li>Inside each &lt;a&gt; tag, add a <code>href="#"</code>.</li>
</ul>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://patrickskinner.tech"</span>
            &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fa-solid fa-globe"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Homepage"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span>&gt;</span>        
            <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
            Official Website
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</code></pre>
<p><em>Note: If you want the links to open on a page in a new tab, add</em> <code>target="_blank"</code> <em>inside the</em> <code>a</code> <em>tag after the</em> <code>href</code><em>.</em></p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://patrickskinner.tech"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
            &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fa-solid fa-globe"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Homepage"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span>&gt;</span>        
            <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
            Official Website
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
</code></pre>
<h2 id="heading-step-8-add-an-image-to-the">STEP 8: Add an Image to the <code>&lt;header&gt;</code></h2>
<p><strong>We're going to use a decentralized file storage application called AKORD.</strong></p>
<ul>
<li><p>Create an account here: https://akord.com/products/web-app</p>
</li>
<li><p>Set up a <strong>PUBLIC</strong> folder.</p>
</li>
<li><p>Place your preferred PFP in the public folder either with the upload button or with drag or drop. <em>Note: I recommend the image be square in dimensions no more than 400x400 pixels.</em></p>
</li>
</ul>
<h3 id="heading-important">IMPORTANT:</h3>
<p><strong><em>The reason why we're using</em> <em>AKORD</em> <em>is that it's a great place to establish website images. You'll never need to pay for the storage monthly and it's stored on the Arweave Blockchain. So, no matter what, the image will always display on the website and you'll avoid storage server issues, payment issues, etc. Now, because it's stored on the blockchain, establishing a working link is slow. Once you upload the image, there will be an orange dot next to the image title. That orange dot means the image is still being stored on the Arweave Blockchain and you won't have a working link for it yet. the process takes roughly 30 mins before it's ready.</em></strong></p>
<ul>
<li><p>Once the orange dot has disappeared, copy the image address by right-clicking the image and clicking 'copy image address'.</p>
</li>
<li><p>Go back to the HTML file and, within the <code>&lt;header&gt;</code> tag just before the <code>&lt;h1&gt;</code> tag, add this: <code>&lt;img src="" /&gt;</code></p>
</li>
<li><p>Inside the <code>src=""</code> inside the double quotes, add the image address that you copied from AKORD.</p>
</li>
</ul>
<pre><code class="lang-xml">      <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">"https://arweave.net/#"</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Your Name<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
</code></pre>
<h3 id="heading-tag-definition">Tag Definition</h3>
<ul>
<li><code>&lt;img /&gt;</code> is an HTML tag used to insert an image into a web page. The <code>img</code> stands for "image" and it is a self-closing tag, meaning that it doesn't require a closing tag. The <code>src</code> attribute is used to specify the URL or file path of the image to be displayed on the web page. Other attributes, such as <code>alt</code> and <code>title</code> can be used to provide a description and additional information about the image for accessibility and SEO purposes.</li>
</ul>
<h2 id="heading-step-9-begin-styling-with-css">Step 9: Begin Styling with CSS</h2>
<ul>
<li><p>Open your 'style.css' file in your text editor.</p>
</li>
<li><p>Style your body &amp; HTML tags with these settings:</p>
</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span>,
<span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Open Sans"</span>, sans-serif;
}
</code></pre>
<ul>
<li>Style your header tags with these settings:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">align-items</span>: center;
}
</code></pre>
<h2 id="heading-step-10-stylizing-the-profile-picture">STEP 10: Stylizing the Profile Picture</h2>
<p><strong>In this section, we're going to use an SVG Shape Generator from Softr.io.</strong></p>
<ul>
<li><p>Go to this website: <a target="_blank" href="https://www.softr.io/tools/svg-shape-generator">https://www.softr.io/tools/svg-shape-generator</a></p>
</li>
<li><p>Use the tool to create a shape you'd like to have as the frame of your PFP.</p>
</li>
<li><p>Once created, download the SVG file <em>(don't copy it)</em>.</p>
</li>
<li><p>With the file downloaded, go to this website: <a target="_blank" href="https://base64.guru/">https://base64.guru/</a></p>
</li>
<li><p>At the top navbar, mouse over "Converter", then "Base64 Encode", then "Image", then click on "SVG".</p>
</li>
<li><p>Change the output to "Data URI -- data:content/type;base64"</p>
</li>
<li><p>Upload the SVG file you downloaded from Softr.io.</p>
</li>
<li><p>Click "Encode SVG to Base64".</p>
</li>
<li><p>A code will appear in a text box labeled "Base64", copy the entire code.</p>
</li>
<li><p>Go back to your text editor and into the style.css file. Create this CSS declaration block.</p>
</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> &gt; <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">10em</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">10em</span>;
  <span class="hljs-attribute">-webkit-mask-image</span>: <span class="hljs-built_in">url</span>();
}
</code></pre>
<ul>
<li>Inside the parenthesis of the <code>url()</code>, paste the code you copied from Base64.guru.</li>
</ul>
<p><strong>Once completed, this will reshape the container of your PFP to the style that you created in the SVG Shape Generator from Softr.io.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679430356273/7913a6b5-667f-4ae4-93ba-b54aafcc63c7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-11-stylizing-your-h1">STEP 11: Stylizing your H1</h2>
<p><strong>We're going to stylize your</strong> <code>H1</code> <strong>to have a pill-shaped container with a white border.</strong></p>
<ul>
<li>Create a new CSS declaration box in your style.css file:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">header</span> &gt; <span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">display</span>: inline-block;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2em</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#000a</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.3em</span> <span class="hljs-number">0.6em</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#fffa</span>;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679429908644/baba9e53-ac6e-4e2e-ba50-99ffe6e6a3dd.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-12stylizing-the-link-buttons">STEP 12:Stylizing the Link Buttons</h2>
<p><strong>Let's first create the pill-shaped container for the link buttons.</strong></p>
<ul>
<li>Create a CSS declaration block for <code>ul &gt; li</code> in the style.css file:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">ul</span> &gt; <span class="hljs-selector-tag">li</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">backdrop-filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">10px</span>) <span class="hljs-built_in">saturate</span>(<span class="hljs-number">160%</span>) <span class="hljs-built_in">contrast</span>(<span class="hljs-number">180%</span>);
  <span class="hljs-attribute">-webkit-backdrop-filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">10px</span>) <span class="hljs-built_in">saturate</span>(<span class="hljs-number">160%</span>) <span class="hljs-built_in">contrast</span>(<span class="hljs-number">180%</span>);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10em</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">1.4em</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">28</span> <span class="hljs-number">32</span> <span class="hljs-number">93</span> / <span class="hljs-number">24%</span>) <span class="hljs-number">0px</span> <span class="hljs-number">2px</span> <span class="hljs-number">8px</span> <span class="hljs-number">0px</span>;
}
</code></pre>
<ul>
<li>Just above that declaration, create the CSS declaration block for <code>ul</code>:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">ul</span> {
  <span class="hljs-attribute">box-sizing</span>: border-box;
  <span class="hljs-attribute">list-style</span>: none;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">2em</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">480px</span>;
}
</code></pre>
<ul>
<li>Below the CSS declaration block for ul &gt; li, create another CSS declaration block for <code>ul &gt; li &gt; a</code>:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">ul</span> &gt; <span class="hljs-selector-tag">li</span> &gt; <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: row;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">0.5em</span>;
}
</code></pre>
<ul>
<li>Lastly, create a CSS declaration block for <code>a</code>:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#000</span>;
}
</code></pre>
<p><strong>The final result should look like this:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679430886995/4599f164-110d-4bb1-8e39-c15966b40502.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-13stylizing-the-background-with-vantajs-motion-graphics">STEP 13:Stylizing the Background with Vanta.js Motion Graphics</h2>
<p><strong>Let's create a background that has paper birds flying in a dark background and are responsive to mouse movement.</strong></p>
<ul>
<li>Create a new CSS declaration box in your style.css file:</li>
</ul>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#000</span>;
  <span class="hljs-attribute">background-size</span>: cover;
}
</code></pre>
<ul>
<li><p>Go to this website: <a target="_blank" href="https://www.vantajs.com/">https://www.vantajs.com/</a></p>
</li>
<li><p>In the design navbar on the right of the screen, change the colors however you want them to be. Once done, copy the code from the bottom of the design navbar.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679431161885/8ded9510-12df-4eff-b268-edeea1dac50c.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-follow-these-next-steps-very-carefully">FOLLOW THESE NEXT STEPS VERY CAREFULLY</h3>
<ul>
<li><p>Go back to your text editor and into your index.html file.</p>
</li>
<li><p>Within your &lt;head&gt; tags, paste the code. under the other elements.</p>
</li>
</ul>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Patrick Skinner - "Doc"<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"'viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"</span>
      <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ=="</span>
      <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>
      <span class="hljs-attr">referrerpolicy</span>=<span class="hljs-string">"no-referrer"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"three.r134.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"vanta.birds.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      VANTA.BIRDS({
        <span class="hljs-attr">el</span>: <span class="hljs-string">"#your-element-selector"</span>,
        <span class="hljs-attr">mouseControls</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">touchControls</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">gyroControls</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">minHeight</span>: <span class="hljs-number">200.0</span>,
        <span class="hljs-attr">minWidth</span>: <span class="hljs-number">200.0</span>,
        <span class="hljs-attr">scale</span>: <span class="hljs-number">1.0</span>,
        <span class="hljs-attr">scaleMobile</span>: <span class="hljs-number">1.0</span>,
      });
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
</code></pre>
<ul>
<li><p>With this code added, it will not work until you change the <code>src</code> links. Go back to the Vanta.js website, right-click each link from the code snippet, and select "Copy Link Address".</p>
</li>
<li><p>Go back to your text editor and into your index.html file. Paste the new link to the appropriate <code>script</code> tag.</p>
</li>
</ul>
<pre><code class="lang-xml">    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.birds.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h1 id="heading-youre-done">YOU'RE DONE!</h1>
<p>Congratulations! Let me know what you think of this build. Feel free to post questions in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[Applying the Orbital Model for DevRels]]></title><description><![CDATA[As a DevRel professional, you know that building relationships with developers and driving adoption of your protocol are key to success. But how do you achieve these goals in a structured and sustainable way? Enter the Orbital Model. This strategic f...]]></description><link>https://blog.patrickskinner.tech/applying-the-orbital-model-for-devrels</link><guid isPermaLink="true">https://blog.patrickskinner.tech/applying-the-orbital-model-for-devrels</guid><category><![CDATA[DevRel]]></category><category><![CDATA[orbitmodel]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Tue, 21 Mar 2023 05:50:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679346498582/631c135c-34f6-4551-a510-7d2b87e007a5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a DevRel professional, you know that building relationships with developers and driving adoption of your protocol are key to success. But how do you achieve these goals in a structured and sustainable way? <strong>Enter the Orbital Model.</strong> This strategic framework focuses on customer engagement, brand reputation, and product innovation - all essential elements in the DevRel world. Keep reading to discover how the Orbital Model can be applied by DevRels for long-term growth and community building.</p>
<h2 id="heading-what-is-the-orbital-model">What is the Orbital Model?</h2>
<p>The Orbital model is a strategic framework that focuses on creating long-term relationships with customers through customer engagement, brand reputation, and product innovation. This model can be applied to a variety of businesses, including open source protocols, and can be especially relevant to the role of Developer Relations (DevRel) in these organizations.</p>
<h2 id="heading-the-four-elements-of-the-orbital-model">The Four Elements of the Orbital Model</h2>
<p>The four key elements of the Orbital Model:</p>
<ol>
<li><p><strong>Gravity</strong>: Gravity is the force that keeps members engaged and motivated in a community. Members revolve around a shared mission and values, building relationships that help the community achieve its goals. The mission, values, and connections act as forces that attract and retain members.</p>
</li>
<li><p><strong>Love</strong>: Love refers to the emotional connection that members feel towards the community and the protocol. This emotional connection drives engagement, advocacy, and loyalty.</p>
</li>
<li><p><strong>Reach</strong>: Reach is the process of expanding the community by attracting new members and increasing the community's visibility. Reach is not just about numbers, but also about the quality of connections and relationships within the community.</p>
</li>
<li><p><strong>Impact</strong>: Impact is the ultimate goal of the Orbital Model. The impact of the community and the protocol is measured by how it affects the lives of its members and the world around it. Impact can be achieved through product innovation, community contributions, and advocacy.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679348058856/5768adb0-4983-4a99-a10f-4a77ee4eab04.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-gravity">Gravity:</h3>
<p>The Orbital Model recognizes the importance of building a strong community and the force of gravity that keeps members engaged and motivated. In a solar system, everything revolves around the sun, while in a community, members revolve around a shared mission and values. As members orbit, they build relationships that help the community achieve its goals, and the mission, values, and connections act as forces that attract and retain members.</p>
<blockquote>
<p>A community's gravity causes members to pull themselves closer to the center, with highly engaged members orbiting faster at the center.</p>
</blockquote>
<p>The concept of gravity is central to community building. These movements determine each community's unique field of gravity. In the Orbital Model, community builders can measure gravity and observe how it changes over time in response to various stimuli, such as engagement tactics and product improvements. Understanding a community's field of gravity is key to building a strong, sustainable community around an open source protocol.</p>
<pre><code class="lang-bash">Gravity = Change <span class="hljs-keyword">in</span> Weighted Commitment / <span class="hljs-comment"># of Members</span>
</code></pre>
<h3 id="heading-gravity-amp-growth">Gravity &amp; Growth:</h3>
<p>Gravity and growth are two essential components of community building. However, striking a balance between growth and maintaining a community's values and sense of connection can be challenging. If a community grows too quickly without maintaining its gravity, it can negatively impact its impact and engagement levels.</p>
<p>Fortunately, the Orbit Model provides a framework for achieving this balance by leveraging the benefits of both gravity and growth. In this model, gravity is seen as the foundation for growth. The more gravity a community has, the more growth it can sustain while maintaining its values and connection between members. When members increase their commitment level, they can guide and integrate new or less involved members, allowing for an efficient and reliable process of moving members up to higher levels of involvement.</p>
<blockquote>
<p>High-gravity communities can effectively integrate new members and help them reach higher levels of involvement. In contrast, low-gravity communities struggle with this process. As a metric, gravity measures how quickly member involvement is changing.</p>
</blockquote>
<p>The Orbit Model identifies two key characteristics of each member, love and reach, that can help increase gravity. Love refers to the emotional connection members feel towards the community and the protocol, while reach is the process of expanding the community and increasing its visibility. By focusing on both love and reach, community builders can increase the community's gravity and sustainably grow its membership while maintaining its sense of connection and values.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679348004886/8247e1a7-7802-495e-9fa9-93c38191ee1c.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-love">Love:</h3>
<p>In the context of community building, love is a crucial measure of a member's level of involvement and commitment. As members progress from newcomers to regulars to leaders, their love for the community increases, and this high level of commitment is essential in directing resources and people towards the company's mission.</p>
<blockquote>
<p>However, it's not just high love members that are important; a balance of members with varying levels of love is necessary for a healthy community. Without a sufficient number of medium and high love members, it's not advisable to add new low-love members since they may struggle to integrate and engage with the community.</p>
</blockquote>
<p>Using love as a metric, members can be assigned to different orbit levels, based on their level of commitment and regularity of activities. These levels provide a quick snapshot of a member's orbit within the community's field of gravity, helping community builders make informed decisions about growth and engagement tactics.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679348027560/e1080aa3-f3dc-465f-a3dc-a405199b1cea.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-reach">Reach:</h3>
<p>Reach is a metric that measures a community member's influence within and beyond the community. It factors in a member's reputation, credibility, and degree of connectedness to others. Members with high reach have broad and influential networks within the community, and information is likely to flow through them, causing others to follow.</p>
<p>To study reach, we use network analysis and graphs. Each node in the graph represents a member, and an edge exists between two nodes if they have a direct or indirect connection, such as a discussion on the community forum or attending the same event.</p>
<p>As a metric, reach considers the number, strength, and freshness of a member's connections, as well as their centrality within the network. Additionally, a concept called clout measures a member's external reach and looks at their influence outside the community, as well as their ability to act as a bridge between different groups of people.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679348164222/d4e07323-4cee-4953-bac0-db762509a9d2.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-impact">Impact:</h3>
<p>The outcomes of a community's work and operation are collectively referred to as its impact. When a business invests in building a community, it can expect a return on its investment in the form of increased revenue, leads, visitors, partners, products, support, and social impact, among others.</p>
<p>In the Orbit Model, we conceptualize the community-to-business process as a flywheel, with the outputs of each step feeding back in as inputs to create a self-sustaining cycle. For instance, when satisfied customers become advocates, they attract more happy customers who, in turn, become advocates themselves, leading to a multiplication of positive outcomes. This helps explain how communities can generate exponential impact over time.</p>
<h2 id="heading-the-standard-open-source-community">The Standard Open Source Community</h2>
<p>Open source communities typically have a core tech stack, including a code repository, synchronous communication for core members, and occasionally a Twitter account or mailing list. These communities possess an inherent understanding of the development process and can usually code. Some well-known open source communities include Mozilla, Linux, freeCodeCamp, VS Code, and Orbit.</p>
<h3 id="heading-common-challenges-they-face">Common Challenges They Face:</h3>
<p>Open source projects have become popular due to the collaborative opportunities they offer. However, working in an open source environment also presents some unique challenges. First and foremost, it is essential to prioritize moderation, having a code of conduct, and creating an inclusive, welcoming environment. These factors are necessary to build a high-gravity community that attracts and retains contributors.</p>
<p>Another aspect that is often overlooked is user support. When a project is new and not widely adopted, it may not seem like a burden. However, as the project grows and gains more users, the number of dependencies also increases. If the project is maintained by a company or a larger organization, it may have an advantage in terms of funding to pay people to maintain the project. Unfortunately, this is not always the case, and getting volunteers to communicate and update user needs can be challenging.</p>
<p>If a project has a good set of contributors and maintainers, it is crucial to cultivate their needs to ensure the project's health and foster a healthy community. However, if these challenges are not taken seriously, the project could lose trust or traction among maintainers, users, and supporters.</p>
<blockquote>
<p>A lack of a healthy community could lead to other alignment problems.</p>
</blockquote>
<p>If there is no agreement on the direction to move in, the project could stall and suffer from a lack of growth.</p>
<p>Creating a high-gravity healthy community requires a diverse set of individuals with different skill sets, knowledge, and ideas. It takes time, energy, and resources to build and maintain a healthy community. By dedicating these resources, you effectively contribute to creating a better internet for everyone.</p>
<h2 id="heading-wrapping-up-with-a-high-gravity-community">Wrapping up with a High-Gravity Community:</h2>
<p>In the open source world, building a high-gravity community is more critical than the project itself. Creating an inclusive and welcoming environment, prioritizing moderation, and providing user support are all key factors in cultivating a healthy community. By rewarding and supporting participants, and educating and encouraging others, we can create lasting connections and relationships that extend beyond the project's scope. Ultimately, the success of an open source project hinges on the strength and health of its community.</p>
<h3 id="heading-references">References:</h3>
<p>The Orbit Model was created by <a target="_blank" href="https://twitter.com/patrickjwoods">Patrick Woods</a> and <a target="_blank" href="https://twitter.com/dzello">Josh Dzielak</a> of Orbit Labs. To learn more about the Orbit Model and Orbit Labs, be sure to check out the links below.  </p>
<p>Orbit Labs: <a target="_blank" href="https://orbit.love">https://orbit.love</a><br />Orbit Model: <a target="_blank" href="https://orbit.love/model">https://orbit.love/model</a></p>
<p>Twitter: <a target="_blank" href="https://twitter.com/OrbitModel">https://twitter.com/OrbitModel</a></p>
]]></content:encoded></item><item><title><![CDATA[DevRel Uni: A Transformative 4-Week Course for Aspiring DevRel Professionals]]></title><description><![CDATA[What is DevRel Uni?
As a software engineer, I've always been fascinated by the power of technology to bridge communication gaps and connect people from all over the world. When I heard about DevRel Uni, a 4-week certifying course that promised to tea...]]></description><link>https://blog.patrickskinner.tech/devrel-uni-a-transformative-4-week-course-for-aspiring-devrel-professionals</link><guid isPermaLink="true">https://blog.patrickskinner.tech/devrel-uni-a-transformative-4-week-course-for-aspiring-devrel-professionals</guid><category><![CDATA[DevRel]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Wed, 15 Mar 2023 20:33:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678831714325/bbd1d66d-f165-47a4-a155-bb925c9d50a0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-devrel-uni">What is DevRel Uni?</h2>
<p>As a software engineer, I've always been fascinated by the power of technology to bridge communication gaps and connect people from all over the world. When I heard about DevRel Uni, a 4-week certifying course that promised to teach the skills needed to excel in the Developer Relations (DevRel) field, I was intrigued.</p>
<p>I've always been drawn to roles that involve both technical and interpersonal skills, and DevRel seemed like the perfect fit. I wanted to learn how to effectively communicate complex technical information to different audiences, including developers, end customers, and non-technical stakeholders. I was also excited to learn how to generate never-ending content ideas and repurpose content to showcase products.</p>
<p>The course was designed to cover all the fundamental aspects of DevRel, and it did not disappoint. We learned about the different technologies and tools required for a successful career in DevRel, as well as the strategies to stay on top of the rapidly changing business landscape.</p>
<h3 id="heading-video-on-devrel-uni"><strong>Video on DevRel Uni:</strong></h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/Q1yTnEw0jR8">https://youtu.be/Q1yTnEw0jR8</a></div>
<p> </p>
<h2 id="heading-the-mentors">The Mentors</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678831973449/607abf14-f2cf-4b15-9131-199753062ac9.png" alt class="image--center mx-auto" /></p>
<p>The stacked mentors alone were reason enough to take this course. Each week had one mentor focusing on specific topics within the DevRel role. Each of the mentors are certifiable GOATs in DevRel. I was actually surprised to see who the mentors were going to be in the very first cohort.</p>
<h2 id="heading-what-each-week-holds">What Each Week Holds</h2>
<h3 id="heading-week-1-understanding-the-devrel-position-content-personal-branding-and-social-media">Week 1: Understanding the DevRel Position | Content, Personal Branding, and Social Media</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678832723711/325efb61-77df-4dbe-b10d-214cd999433f.png" alt class="image--center mx-auto" /></p>
<p>In the first week, participants learned about the DevRel position and what to expect while working as a DevRel directly from the creator of DevRel Uni, Bianca Buzea (Twitter: <a target="_blank" href="https://twitter.com/buzea200">@buzea200</a>). Bianca has years of experience working as a DevRel and has put together an amazing course with some of the most iconic DevRels in the industry.</p>
<p>Her experience as a DevRel really shined while she guided the 1st cohort through the program. 10/10 would take another course from her.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678832656152/724979c4-6a52-4815-a2df-cf17f9e3226b.png" alt class="image--center mx-auto" /></p>
<p>Nader Dabit (Twitter: <a target="_blank" href="https://twitter.com/dabit3">@dabit3</a>), one of the most iconic DevRels in the industry, taught the importance of content, personal branding, and social media. His entire philosophy on being a "Bridge Builder" and not a Gatekeeper was what really sold many on wanting to become a DevRel. Nader shared a poem called "The Bridge Builder" by Will Allen Dromgoole that serves as a reminder of what it means to be a DevRel advocate. I even printed the poem and hung it as a wall as to what I aspire to emulate as continue to pursue a career in DevRel.</p>
<h3 id="heading-week-2-documentation-strategy">Week 2: Documentation Strategy</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678832447971/50ef2bcc-9fa6-47cd-a329-7f9e35917a94.png" alt class="image--center mx-auto" /></p>
<p>Michiel Mulders (Twitter: <a target="_blank" href="https://twitter.com/michiel_mulders?lang=en">@michiel_mulders</a>), the Developer Advocate and Documentation Strategist at Hedera Hashgraph, provided a deep dive into the importance of documentation strategy. Everyone learned about the various tools available for documentation, best practices, and how to attract more builders using the documentation. He even shared some of his own strategies to create documentation that is both informative and attractive to developers.</p>
<h3 id="heading-week-3-strategies-kpis-and-metrics">Week 3: Strategies, KPIs, and Metrics</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678832556629/8b96bd04-ca30-4915-88a0-98ab1551223e.png" alt class="image--center mx-auto" /></p>
<p>Vitto Rivabella (Twitter: <a target="_blank" href="https://twitter.com/VittoStack">@VittoStack</a>), who I'm officially dubbing as "The Gary V of Web3," spoke about strategies, KPIs, and metrics. We learned about the importance of KPIs while being reminded to never dwell or operate solely on them. No wonder Vitto is so well known in the Web3 space. He could talk about anything and I'd feel energized afterward. I'm definitely keeping a close eye on his schedule to see if I can catch him speaking live.</p>
<h3 id="heading-week-4-event-strategies">Week 4: Event Strategies</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678832597436/832feb67-1358-4f03-a44c-e58573aa03f8.png" alt class="image--center mx-auto" /></p>
<p>The final week of the course ended with the one and only Steph Orpilla (Twitter: <a target="_blank" href="https://twitter.com/Oceans404">@Oceans404</a>), a Developer Relations Engineer at Polygon. Steph spoke about event strategies such as meetups, hackathons, and conferences. Her unique approach to every event differently and giving herself unique goals to achieve at each event was very enlightening as to how we should approach live and online events.</p>
<h2 id="heading-the-course-work">The Course Work</h2>
<p>The coursework revolved around a specific open-source project of each participant's choice, encouraging creativity and engagement with the wider tech community. Many students were even directly connected to the DevRel teams of the protocols they had chosen, leading to potential job placements within DevRel teams.</p>
<h2 id="heading-overall-experience">Overall Experience</h2>
<p>For the 1st cohort of DevRel Uni, the experience was 10/10 stars. Participants quickly became connected with each other and still communicate actively despite the program being done. The knowledge gained in this course has transformed what many want to do with their tech careers. If you're looking for a position that truly feels right for you, DevRel might be it.</p>
<h2 id="heading-want-to-join-the-next-cohort">Want to Join the Next Cohort?</h2>
<p>Signups for the 2nd Cohort of DevRel Uni were announced TODAY (March 14, 2023). Here is a link to <a target="_blank" href="https://docs.google.com/forms/d/e/1FAIpQLSfbg-t4QzVnC1WhGUtMmAGYBJ__1yWz18z8WcBv01s0u2Qf1A/viewform"><strong>SIGN UP NOW!</strong></a> Remember, spots for the 1st cohort filled up very quickly, so be sure to sign up quickly!</p>
]]></content:encoded></item><item><title><![CDATA[How I built a DALL-E Clone using MERN]]></title><description><![CDATA[GitHub Repository: https://github.com/PSkinnerTech/aureliusai-art
Artificial intelligence (AI) is transforming the way we interact with technology, and the possibilities are limitless. One of the most exciting developments in the field of AI is the c...]]></description><link>https://blog.patrickskinner.tech/how-i-built-a-dall-e-clone-using-mern</link><guid isPermaLink="true">https://blog.patrickskinner.tech/how-i-built-a-dall-e-clone-using-mern</guid><category><![CDATA[openai]]></category><category><![CDATA[MERN Stack]]></category><category><![CDATA[Dall-e2]]></category><category><![CDATA[vite]]></category><category><![CDATA[Tailwind CSS]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Fri, 03 Mar 2023 15:11:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1677855524186/a3943579-a80b-403f-9e4f-ce061980276a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>GitHub Repository:</strong> <a target="_blank" href="https://github.com/PSkinnerTech/aureliusai-art">https://github.com/PSkinnerTech/aureliusai-art</a></p>
<p>Artificial intelligence (AI) is transforming the way we interact with technology, and the possibilities are limitless. One of the most exciting developments in the field of AI is the creation of image generation models like DALL-E. DALL-E, developed by OpenAI, can generate images from textual prompts, creating images that have never existed before. In this blog, I will be discussing how I built a DALL-E image generator clone using React JS, ExpressJS, MongoDB, Cloudinary, and Node.js.</p>
<p><em>Note: I later plan on building in an option to use DALL-E or MidJourney as the AI model. When I do, I'll be sure to edit this blog with an update. (Mar 3rd, 2023)</em></p>
<h2 id="heading-building-the-frontend">Building the Frontend</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677855586457/54b45e20-96c0-4b06-9943-b3f40c053723.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-vitejs">Vite.js</h3>
<p>To start, I built the frontend of the application using React JS. Instead of directly using "create-react-app," I decided to experiment with <a target="_blank" href="https://vitejs.dev/">Vite.js</a> as the developmental environment. Vite.js allowed me to set up a development environment for frameworks like React and Vue and even for Vanilla JavaScript apps with a dev server. Moreover, it allowed me to hot reload in just three commands. Vite supports Rollup.js internally for bundling, making it a powerful tool for building large-scale applications.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677855622963/6b4e185f-36c2-4394-8828-4219cc438127.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-tailwindcss">Tailwind.css</h3>
<p>Once I set up Vite.js, I set up my CSS framework with <a target="_blank" href="https://tailwindcss.com/">Tailwind.css</a>. Tailwind CSS is an open-source CSS framework. The main feature of this library is that, unlike other CSS frameworks like Bootstrap, it does not provide a series of predefined classes for elements such as buttons or tables. Instead, it provides a set of utility classes that can be used to style any element. This makes it more flexible and customizable than other CSS frameworks.</p>
<p>After setting up my app and CSS framework, I built my src folder. This included three main folders: assets, components, and pages. The assets folder contains all of my image assets, the components folder contains all of my major page snippets (Image Cards, Image Generator's Form Field, and the loading features), and the pages folder contains my two main pages (Home.jsx and CreateImage.jsx).</p>
<h2 id="heading-building-the-backend">Building the Backend</h2>
<p>Once I had all of my UI built for my image generator, I started building into the backend (server folder). I set up my database using MongoDB, my image storage for my community page using Cloudinary, and then my OpenAI API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677855664085/bdae6ec5-ef23-4625-8633-1ad989e14ea6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-mongodb">MongoDB</h3>
<p>Setting up <a target="_blank" href="https://www.mongodb.com/">MongoDB</a> is a very easy and straightforward process. MongoDB is a NoSQL database, which means it stores data in a JSON-like format. It is scalable and flexible, making it an excellent choice for applications that require large amounts of data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677855712230/cbd8d79a-4471-428f-ab3e-89ef6c00403b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-cloudinary">Cloudinary</h3>
<p>Setting up <a target="_blank" href="https://cloudinary.com/">Cloudinary</a> was a bit more challenging. Cloudinary is a cloud-based image and video management solution that provides a range of features for managing, manipulating, and delivering images and videos. It took me some time to figure out how to set up the Cloudinary API and how to use it with my application. However, once I had it set up, it was a breeze to use.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677855804445/1114767a-5419-4008-bf99-8e1405ce7568.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-openai-the-most-painful-part">OpenAI: The Most Painful Part</h3>
<p><a target="_blank" href="https://openai.com/">OpenAI</a> has been the most challenging part of building this application. Despite being used by millions, OpenAI still has quite a few issues. It wasn't until I signed up for their API status notifications that I realized the API probably goes down almost every other day, if not daily. One of the first issues that didn't have an immediately obvious fix was when I started getting a <strong><em>"402: billing ha"</em></strong> error every time I tried to generate an image. It took me two days to realize that the $18 of free API calls you receive when creating your OpenAI account had expired.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677856031536/6706da44-2042-4b72-9ed7-8759b398d008.jpeg" alt class="image--center mx-auto" /></p>
<p><strong>Update Request?</strong></p>
<p>I really wish there was something more obvious in your account to let you know that your trial balance was expired. To check if your balance is expired, you have to go into your account's billing to find it. If it is expired, just create a new account and set up an API Key with the new account.</p>
<h2 id="heading-connecting-the-frontend-and-backend">Connecting the Frontend and Backend</h2>
<p>Once I had completed building the frontend and backend separately, it was time to connect the two. This requires a bit of troubleshooting, but it is not too difficult if you know what you're doing.</p>
<p>First, I needed to install the <a target="_blank" href="https://axios-http.com/docs/intro">axios</a> package in my frontend to make HTTP requests to the backend. Axios is a promise-based HTTP client that makes it easy to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.</p>
<p>After installing Axios, I created a config file that contained the base URL of my backend server. This allowed me to make requests to my backend server from my frontend.</p>
<p>Next, I had to create the endpoints in my backend server that would handle the requests from the frontend. I created two endpoints: one to handle requests to generate an image and one to handle requests to save an image to the database.</p>
<p>The dalleRoutes.js handles image generation:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">'dotenv'</span>;
<span class="hljs-keyword">import</span> { Configuration, OpenAIApi } <span class="hljs-keyword">from</span> <span class="hljs-string">'openai'</span>;

dotenv.config();

<span class="hljs-keyword">const</span> router = express.Router();

<span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration({
  <span class="hljs-attr">apiKey</span>: process.env.OPENAI_API_KEY,
});

<span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAIApi(configuration);

router.route(<span class="hljs-string">'/'</span>).get(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Hello from DALL-E!'</span> });
});

router.route(<span class="hljs-string">'/'</span>).post(<span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { prompt } = req.body;

    <span class="hljs-keyword">const</span> aiResponse = <span class="hljs-keyword">await</span> openai.createImage({
      prompt,
      <span class="hljs-attr">n</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">size</span>: <span class="hljs-string">'1024x1024'</span>,
      <span class="hljs-attr">response_format</span>: <span class="hljs-string">'b64_json'</span>,
    });

    <span class="hljs-keyword">const</span> image = aiResponse.data.data[<span class="hljs-number">0</span>].b64_json;
    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">photo</span>: image });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(error);
    res
      .status(<span class="hljs-number">500</span>)
      .send(error?.response.data.error.message || <span class="hljs-string">'Something went wrong'</span>);
  }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre>
<p>The postRoutes.js file handles storing the image to Cloudinary and posting them on the Community Page:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">'dotenv'</span>;
<span class="hljs-keyword">import</span> { Configuration, OpenAIApi } <span class="hljs-keyword">from</span> <span class="hljs-string">'openai'</span>;

dotenv.config();

<span class="hljs-keyword">const</span> router = express.Router();

<span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration({
  <span class="hljs-attr">apiKey</span>: process.env.OPENAI_API_KEY,
});

<span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAIApi(configuration);

router.route(<span class="hljs-string">'/'</span>).get(<span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Hello from DALL-E!'</span> });
});

router.route(<span class="hljs-string">'/'</span>).post(<span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { prompt } = req.body;

    <span class="hljs-keyword">const</span> aiResponse = <span class="hljs-keyword">await</span> openai.createImage({
      prompt,
      <span class="hljs-attr">n</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">size</span>: <span class="hljs-string">'1024x1024'</span>,
      <span class="hljs-attr">response_format</span>: <span class="hljs-string">'b64_json'</span>,
    });

    <span class="hljs-keyword">const</span> image = aiResponse.data.data[<span class="hljs-number">0</span>].b64_json;
    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">photo</span>: image });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(error);
    res
      .status(<span class="hljs-number">500</span>)
      .send(error?.response.data.error.message || <span class="hljs-string">'Something went wrong'</span>);
  }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre>
<h3 id="heading-testing-apis-with-postman">Testing APIs with Postman</h3>
<p>Once I had created the endpoints, I tested them using <a target="_blank" href="https://www.postman.com/">Postman</a> to make sure that they were working properly. Postman is a popular tool used for testing API endpoints.</p>
<p>With the endpoints working properly, it was time to integrate them into the frontend. I created a form in the frontend that would allow users to input the text prompt for the image they wanted to generate. When the form was submitted, the input was sent to the backend server using Axios.</p>
<p>When the backend server received the request, it used the OpenAI API to generate an image based on the user's input. The image was then saved to Cloudinary and the URL was returned to the frontend. The frontend then displayed the generated image to the user.</p>
<h3 id="heading-surprise-me">"Surprise Me"</h3>
<p>I also built a "Surprise Me" feature, that allowed a user to click "surprise me" for a randomly generated prompt rather than creating their own prompt. This is a simple list of 50 prompt options that are selected using a Math.random() function.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> FileSaver <span class="hljs-keyword">from</span> <span class="hljs-string">'file-saver'</span>;

<span class="hljs-keyword">import</span> { surpriseMePrompts } <span class="hljs-keyword">from</span> <span class="hljs-string">'../constants'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRandomPrompt</span>(<span class="hljs-params">prompt</span>) </span>{
  <span class="hljs-keyword">const</span> randomIndex = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * surpriseMePrompts.length);
  <span class="hljs-keyword">const</span> randomPrompt = surpriseMePrompts[randomIndex];

  <span class="hljs-keyword">if</span> (randomPrompt === prompt) <span class="hljs-keyword">return</span> getRandomPrompt(prompt);

  <span class="hljs-keyword">return</span> randomPrompt;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadImage</span>(<span class="hljs-params">_id, photo</span>) </span>{
  FileSaver.saveAs(photo, <span class="hljs-string">`download-<span class="hljs-subst">${_id}</span>.png`</span>);
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building this DALL-E Image Generator Clone was a great learning experience. It allowed me to work with some of the most popular and cutting-edge technologies in web development and gave me a taste of what it's like to build an AI-powered app.</p>
<p>The potential for AI-powered apps and scalable SaaS companies is immense, and I look forward to seeing what innovative solutions developers will come up with in the years to come.</p>
]]></content:encoded></item><item><title><![CDATA[Building a Sleek and Lightweight Portfolio Website with HTML/CSS/Javascript]]></title><description><![CDATA[Github Repository: https://github.com/PSkinnerTech/pskinnertech-cvLive Link: https://elaborate-melba-f7f270.netlify.app/
Building a portfolio website is an essential task for every developer, and it requires a combination of technical skills and crea...]]></description><link>https://blog.patrickskinner.tech/building-a-sleek-and-lightweight-portfolio-website-with-htmlcssjavascript</link><guid isPermaLink="true">https://blog.patrickskinner.tech/building-a-sleek-and-lightweight-portfolio-website-with-htmlcssjavascript</guid><category><![CDATA[HTML5]]></category><category><![CDATA[CSS]]></category><category><![CDATA[email.js]]></category><category><![CDATA[portfolio]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Wed, 01 Mar 2023 22:03:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1677623911757/bb548480-67ab-40ce-8c62-332d3b061107.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Github Repository: <a target="_blank" href="https://github.com/PSkinnerTech/pskinnertech-cv">https://github.com/PSkinnerTech/pskinnertech-cv</a><br />Live Link: <a target="_blank" href="https://elaborate-melba-f7f270.netlify.app/">https://elaborate-melba-f7f270.netlify.app/</a></p>
<p>Building a portfolio website is an essential task for every developer, and it requires a combination of technical skills and creativity. As a software engineer, I wanted to build my own portfolio using basic HTML/CSS/JavaScript while also creating a visually appealing and user-friendly website that can be accessed from any device.</p>
<p><a target="_blank" href="https://akord.com/products/web-app"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677624554106/ce32c174-7d95-427b-82bd-597a567f88f6.png" alt class="image--center mx-auto" /></a></p>
<h2 id="heading-image-storage-method">Image Storage Method</h2>
<p>One of the unique features of my portfolio website is that all the images are actually stored on the <a target="_blank" href="https://www.arweave.org/">Arweave blockchain</a>. This means that the images are available to me forever, removing any concerns about how the files are stored and whether the links to the files are ever at risk of breaking. I used an app called <a target="_blank" href="https://akord.com/products/web-app">Akord</a> to provide a seamless user experience for storing and publishing files on Arweave's blockchain.</p>
<h2 id="heading-graphics">Graphics</h2>
<p>Another important aspect of my portfolio website is the graphics. I created all the graphics myself using Adobe Photoshop and/or Adobe Illustrator. To make the logo spin, I used <a target="_blank" href="https://webkit.org/blog/324/css-animation-2/#:~:text=A%20%40%2Dwebkit%2Dkeyframes%20block,interpolate%20style%20between%20the%20keyframes.">CSS Webkit Keyframes</a>.</p>
<h3 id="heading-spinning-logo-css">Spinning Logo CSS</h3>
<pre><code class="lang-css"><span class="hljs-selector-class">.prt_logo_wrapper</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">2</span>;
  <span class="hljs-attribute">-webkit-animation</span>: rotate <span class="hljs-number">2s</span> linear infinite;
  <span class="hljs-attribute">-moz-animation</span>: rotate <span class="hljs-number">2s</span> linear infinite;
  <span class="hljs-attribute">-o-animation</span>: rotate <span class="hljs-number">2s</span> linear infinite;
  <span class="hljs-attribute">-ms-animation</span>: rotate <span class="hljs-number">2s</span> linear infinite;
  <span class="hljs-attribute">animation</span>: rotate <span class="hljs-number">2s</span> linear infinite;
}
<span class="hljs-keyword">@-webkit-keyframes</span> rotate {
  100% {
    <span class="hljs-attribute">-webkit-transform</span>: <span class="hljs-built_in">rotatey</span>(<span class="hljs-number">360deg</span>);
  }
}
<span class="hljs-keyword">@-moz-keyframes</span> rotate {
  100% {
    <span class="hljs-attribute">-moz-transform</span>: <span class="hljs-built_in">rotatey</span>(<span class="hljs-number">360deg</span>);
  }
}
<span class="hljs-keyword">@keyframes</span> rotate {
  100% {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotatey</span>(<span class="hljs-number">360deg</span>);
  }
}
</code></pre>
<p>The logo is just a PNG file that I created in illustrator, and the loader is a GIF file that I made with the logo using <a target="_blank" href="http://ezgif.com">ezgif.com</a>.</p>
<p><a target="_blank" href="https://ezgif.com"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677624841912/d2e51212-9e06-4dd6-b750-4e8e4d1e0183.png" alt class="image--center mx-auto" /></a></p>
<p>I also took the photos of myself using my professional camera (Sony A7RIII with a Sony 50mm 1.8f Prime Lens) and edited them in Photoshop, leveraging my 4 years of experience as a freelance photographer.</p>
<h2 id="heading-html-structure">HTML Structure</h2>
<p>I had to decide on the layout and structure of my website. I wanted a one-page website with different sections for my portfolio, about me, contact information, and my strengths. However, I also wanted to make it look like a multi-page website by using vertical and horizontal sliders to navigate between sections. Additionally, you'll notice that my website actually has no &lt;header&gt;. To make it slide smoothly from section to section, I decided to go without that and independently add the footer into each necessary section.</p>
<h3 id="heading-main-wrapper">Main Wrapper</h3>
<p>Once I had a clear vision for my website's layout, I started coding the HTML structure. I created a main wrapper that loads after the loader is complete, featuring a picture of me and the text "I'm Patrick Skinner." To add a fun touch, I included an image of my daughter photo-bombing me during my headshot session, hidden within the text.</p>
<p><strong>Home Page Div</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_home_wrapper"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_logo_wrapper"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/header/logo.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Logo"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"prt_close_tab"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_menu_wrapper"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#about"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_top"</span>&gt;</span>about<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_right"</span>&gt;</span>contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#services"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_bottom"</span>&gt;</span>strength<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#portfolio"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_left"</span>&gt;</span>portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                        <span class="hljs-attr">class</span>=<span class="hljs-string">"col-xl-6 offset-xl-6 col-lg-6 offset-lg-6 col-md-5 offset-md-5 col-sm-10 offset-sm-2 col-xs-12 offset-xs-0"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>I`m Patrick Skinner<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p><strong>Supporting CSS</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-class">.prt_menu_wrapper</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.prt_top</span> {
  <span class="hljs-attribute">top</span>: <span class="hljs-number">45px</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0px</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100px</span>;
}
<span class="hljs-selector-class">.prt_menu_wrapper</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.prt_bottom</span> {
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">45px</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0px</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">150px</span>;
}
<span class="hljs-selector-class">.prt_menu_wrapper</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.prt_left</span> {
  <span class="hljs-attribute">top</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">left</span>: -<span class="hljs-number">15px</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotateZ</span>(-<span class="hljs-number">90deg</span>);
  <span class="hljs-attribute">margin-top</span>: -<span class="hljs-number">15px</span>;
}
<span class="hljs-selector-class">.prt_menu_wrapper</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.prt_right</span> {
  <span class="hljs-attribute">top</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0px</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotateZ</span>(<span class="hljs-number">90deg</span>);
  <span class="hljs-attribute">margin-top</span>: -<span class="hljs-number">15px</span>;
}
</code></pre>
<h3 id="heading-about-wrapper">About Wrapper</h3>
<p>Next, I moved on to the "about" section, which I kept simple and straightforward. I wanted to highlight my educational background and work experience, and I incorporated some mouseover functions to add some interactivity.</p>
<h3 id="heading-portfolio-wrapper">Portfolio Wrapper</h3>
<p>The portfolio section was a bit more challenging, as I wanted to showcase my recent projects while keeping the UX simple and intuitive. I decided to feature my AI-powered web apps and my blog, and I used HTML/CSS to create image overlays that provide a brief description of each project when hovered over.</p>
<h3 id="heading-strength-wrapper">Strength Wrapper</h3>
<p>For the "strengths" section, I wanted to create an eye-catching animation to showcase my skills. I used an appear() function within the custom.js file to create the animation, and I included images formatted in illustrator and hosted on the Arweave blockchain.</p>
<h3 id="heading-contact-wrapper">Contact Wrapper</h3>
<p>Finally, I added a contact section and used a Javascript function from EmailJS to handle my incoming contact requests.</p>
<pre><code class="lang-xml">//Within the <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span> of index.html

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>&gt;</span><span class="javascript">
   (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
      emailjs.init(<span class="hljs-string">"&lt;EmailJS Account Public Key&gt;"</span>);
   })();
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

//Contact Form

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_contact_info"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-12"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>CONTACT ME:<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>For hiring or other inquires, fill out the form below.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Name"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Email"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Message"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"4"</span> <span class="hljs-attr">required</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prt_btn submitForm"</span> <span class="hljs-attr">form-type</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"sendMail(); reset(); return false"</span>&gt;</span>Send<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">//Within index.js showing the contact form handling function</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMail</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">var</span> params = {
    <span class="hljs-attr">name</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"name"</span>).value,
    <span class="hljs-attr">email</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"email"</span>).value,
    <span class="hljs-attr">message</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"message"</span>).value,
  };
  <span class="hljs-keyword">const</span> serviceID = <span class="hljs-string">"&lt;EmailJS Account Public ID&gt;"</span>;
  <span class="hljs-keyword">const</span> templateID = <span class="hljs-string">"&lt;EmailJS Template Public ID&gt;"</span>;

  emailjs
    .send(serviceID, templateID, params)
    .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"name"</span>).value = <span class="hljs-string">""</span>;
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"email"</span>).value = <span class="hljs-string">""</span>;
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"message"</span>).value = <span class="hljs-string">""</span>;
      <span class="hljs-built_in">console</span>.log(res);
      alert(<span class="hljs-string">"Message sent successfully!"</span>);
    })
    .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(err));
}
</code></pre>
<p><em>Note: This form is a very simple and straightforward approach, so I plan on adding to my portfolio a custom Typeform clone. So, stay tuned.</em></p>
<h2 id="heading-closing-comments">Closing Comments</h2>
<p>Overall, building my portfolio website was a fun experience that allowed me to showcase my technical and creative skills. Being able to add the Easter Egg image of my daughter photo-bombing my photoshoot while still maintaining a very professional feel to the website allows me to display my ability to maintain a highly professional approach to my work while still allowing my creativity and personality to flow naturally within my work. Let me know what you thought about this website and if you have any questions about its build.</p>
]]></content:encoded></item><item><title><![CDATA[Building with OpenAI's API: The Birth of Aurelius AI]]></title><description><![CDATA[Test Site

GitHub Repository



Like thousands of others, I recently jumped into building with OpenAI's API. It's amazing what AI can do, right? After building a few AI tools for myself and others, I decided to start Aurelius AI with my friend Patric...]]></description><link>https://blog.patrickskinner.tech/building-with-openais-api-the-birth-of-aurelius-ai</link><guid isPermaLink="true">https://blog.patrickskinner.tech/building-with-openais-api-the-birth-of-aurelius-ai</guid><category><![CDATA[openai]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[React]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[playgroundai]]></category><dc:creator><![CDATA[Patrick Skinner]]></dc:creator><pubDate>Thu, 23 Feb 2023 19:30:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1677179441587/25bf0cc2-21d7-48d3-a1a6-c91be3c76d54.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<ul>
<li><p><a target="_blank" href="https://transcendent-babka-b2a340.netlify.app/">Test Site</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/PSkinnerTech/project_aurelius-site">GitHub Repository</a></p>
</li>
</ul>
<hr />
<p>Like thousands of others, I recently jumped into building with OpenAI's API. It's amazing what AI can do, right? After building a few AI tools for myself and others, I decided to start Aurelius AI with my friend Patrick Burt. Aurelius AI is a service-based company that builds custom AI tools for small to medium-sized businesses.</p>
<p>Now, to the fun part.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677179464233/72d7853b-a1e3-40cd-9ff4-49ac723555ee.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-building-aurelius-ais-website-with-ai">Building Aurelius AI's Website with AI</h2>
<p>When building the website, I wanted to do so using AI alone. It was a bit of a wild idea, but I'm the kind of person who likes to push boundaries. So, I used a combination of ChatGPT and GitHub CoPilot. Let me tell you, these tools are very powerful, but I definitely couldn't have done it without a really strong understanding of React JS, HTML5, CSS3, and prompt engineering.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677178527587/0502a4eb-623f-4c1a-b093-bd5df44b996b.gif" alt="GitHub CoPilot being used to create CSS3 text." class="image--center mx-auto" /></p>
<p>Now, don't get me wrong, using AI for website development was a mixed experience. There were areas where it was super helpful, but it wasn't a complete solution. I had to manually input content for some sections of the website since the AI couldn't generate the desired output. And, while GitHub CoPilot was able to generate code for some of the more tedious tasks, I had to tweak it a lot to get it to work properly.</p>
<p>But, hey, using AI was still super cool! One area where it was particularly useful was in the creation of our logo. I designed it myself, but I used <a target="_blank" href="https://playgroundai.com">Playground AI</a> for inspiration. Playground AI is a tool that lets you experiment with AI-generated artwork. I used it to create a poly-style Marcus Aurelius bust that I later turned into a 3D model that does mouse-tracking. It was a fun project that really showcased the creative potential of AI.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677178655550/7e41835c-c639-4895-8fc0-b29a8b63f914.png" alt class="image--center mx-auto" /></p>
<p>Now, prompt engineering on Playground AI was easier said than done. I think I'll be spending some time watching videos on how to properly use Playground AI. Comparatively, I'd like to try to use <a target="_blank" href="https://midjourney.com/">MidJourney AI</a> to see if I prefer using that over Playground AI. I do have to say, the UI for Playground is extremely friendly and will probably garner more users over time.</p>
<p>Overall, using OpenAI's API to build our website was definitely a challenge, but it was also a really cool learning experience. AI has tremendous potential to transform businesses, and companies like Aurelius AI are at the forefront of this transformation. And, who knows, maybe you'll be inspired to try using AI in your next project too!</p>
<p>If you're interested in seeing the GitHub Repository for this website, feel free to check it out here: <a target="_blank" href="https://github.com/PSkinnerTech/project_aurelius-site">https://github.com/PSkinnerTech/project_aurelius-site</a></p>
<p>The website hasn't been deployed to its own domain just yet, but feel free to check it out here at this Netlify link: <a target="_blank" href="https://transcendent-babka-b2a340.netlify.app/">https://transcendent-babka-b2a340.netlify.app/</a></p>
]]></content:encoded></item></channel></rss>