Skip to main content

Overview

The portfolio content is defined in config.yaml using YAML format. The configuration is loaded on server startup via config.Load("config.yaml").

Config Structure

Root Config Object

config/config.go:9-17
type Config struct {
    Name     string          `yaml:"name"`
    Title    string          `yaml:"title"`
    ASCIIArt string          `yaml:"ascii_art"`
    Intro    string          `yaml:"intro"`
    Skills   []SkillCategory `yaml:"skills"`
    Projects []Project       `yaml:"projects"`
    Contact  Contact         `yaml:"contact"`
}
name
string
required
Your full name. Displayed in the portfolio.Example: "Abdul Sattar"
title
string
required
Your professional title or role.Example: "Sr. Full-stack Developer"
ascii_art
string
required
ASCII art displayed on the home page. Use YAML block scalar (|) for multi-line art.Example:
ascii_art: |
  ███████╗ █████╗ ████████╗████████╗ █████╗ ██████╗
  ██╔════╝██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔══██╗
  ███████╗███████║   ██║      ██║   ███████║██████╔╝
intro
string
required
Introduction text shown on the home page below your title. Use | for multi-line text.Example:
intro: |
  Hi, I am Abdul Sattar!
  Passionate full-stack developer with over 5 years of experience.
skills
SkillCategory[]
required
Array of skill categories. Each category contains related skills.See SkillCategory for structure.
projects
Project[]
required
Array of projects to showcase.See Project for structure.
contact
Contact
required
Contact information and social links.See Contact for structure.

SkillCategory

config/config.go:19-22
type SkillCategory struct {
    Category string      `yaml:"category"`
    Items    []SkillItem `yaml:"items"`
}
category
string
required
Name of the skill category.Example: "Languages", "Frontend", "DevOps"
items
SkillItem[]
required
List of skills in this category.See SkillItem for structure.

SkillItem

config/config.go:24-27
type SkillItem struct {
    Name  string `yaml:"name"`
    Level int    `yaml:"level"` // 0 = no bar, 1-5 = strength bar
}
SkillItem has flexible YAML syntax - can be a plain string or an object:
name
string
required
Name of the skill.Example: "TypeScript", "Docker", "React"
level
int
default:"0"
Skill proficiency level:
  • 0 - Display as bullet point (no progress bar)
  • 1-5 - Display with progress bar (1 = beginner, 5 = expert)
Note: This field is optional when using object syntax.

SkillItem Syntax Options

Option 1: String (no level indicator):
items:
  - "React / Next.js / Vite"
  - "Docker & Compose"
Option 2: Object with level:
items:
  - name: "C#"
    level: 5
  - name: "Python"
    level: 3
Custom YAML Unmarshaling:
config/config.go:29-46
func (s *SkillItem) UnmarshalYAML(unmarshal func(interface{}) error) error {
    // Try plain string first
    var str string
    if err := unmarshal(&str); err == nil {
        s.Name = str
        s.Level = 0
        return nil
    }
    // Otherwise parse as struct
    type raw SkillItem
    var r raw
    if err := unmarshal(&r); err != nil {
        return err
    }
    *s = SkillItem(r)
    return nil
}

Project

config/config.go:48-53
type Project struct {
    Name        string   `yaml:"name"`
    Description string   `yaml:"description"`
    Tech        []string `yaml:"tech"`
    URL         string   `yaml:"url"`
}
name
string
required
Project name.Example: "LogUI", "2ndLock"
description
string
required
Project description (1-2 sentences recommended).Example: "Real-time logging and application monitoring platform."
tech
string[]
required
Array of technologies used in the project.Example: ["NodeJS", "Redis", "Clickhouse"]
url
string
required
Project URL or homepage.Example: "https://logui.xyz"

Contact

config/config.go:55-61
type Contact struct {
    Email    string `yaml:"email"`
    GitHub   string `yaml:"github"`
    LinkedIn string `yaml:"linkedin"`
    Website  string `yaml:"website"`
    Twitter  string `yaml:"twitter"`
}
email
string
Email address.Example: "[email protected]"
github
string
GitHub profile URL.Example: "https://github.com/AB-70"
linkedin
string
LinkedIn profile URL.Example: "https://linkedin.com/in/abdul70"
website
string
Personal website URL.Example: "https://sattar.dev"
twitter
string
Twitter/X profile URL.Example: "https://twitter.com/i0x46"
All contact fields are optional. Empty fields will not be displayed in the UI.

Complete Example

Here’s a full working example from the codebase:
config.yaml
name: "Abdul Sattar"
title: "Sr. Full-stack Developer"
ascii_art: |
  ███████╗ █████╗ ████████╗████████╗ █████╗ ██████╗
  ██╔════╝██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔══██╗
  ███████╗███████║   ██║      ██║   ███████║██████╔╝
  ╚════██║██╔══██║   ██║      ██║   ██╔══██║██╔══██╗
  ███████║██║  ██║   ██║      ██║   ██║  ██║██║  ██║
  ╚══════╝╚═╝  ╚═╝   ╚═╝      ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝

intro: |
  Hi, I am Abdul Sattar!
  Passionate full-stack developer with over 
  5 years of practical experience developing 
  desktop, web, and mobile apps.

  Navigate with arrow keys and explore my projects.

skills:
  - category: "Languages"
    items:
      - name: "C#"
        level: 5
      - name: "TypeScript"
        level: 5
      - name: "JavaScript"
        level: 5
      - name: "Python"
        level: 3

  - category: "Frontend"
    items:
      - "React / Next.js / Vite"
      - "Electron"
      - "ReactNative"
      - "Material UI"
      - "Tailwind CSS"
      - "Framer Motion (framer-motion)"

  - category: "Backend"
    items:
      - "Node.js"
      - "Express"
      - ".NET Core"
      - "MongoDB"
      - "Redis"
      - "Clickhouse"

  - category: "DevOps"
    items:
      - "Docker & Compose"
      - "Kubernetes (GKE, & Self-hosted)"
      - "GitHub Actions"
      - "GitLab Pipelines"
      - "AWS / GCloud"

projects:
  - name: "2ndLock"
    description: "My personal alternative to Bitwarden - A cross-platform secure password vault with E-2-E Encryption and Shared Vaults."
    tech: ["NodeJS", "React", "Electron", "ReactNative"]
    url: "https://2ndLock.xyz"

  - name: "LogUI"
    description: "Real-time logging and application monitoring platform. LogUI provides smart log grouping, AI insights, and API Monitoring in a single dashboard."
    tech: ["NodeJS", "Redis", "Clickhouse"]
    url: "https://logui.xyz"

  - name: "Hjälp"
    description: "Multi-channel helpdesk software that brings together LiveChat, Tickets, Email, and AI-powered support. Manage your entire customer service operation from one beautiful dashboard."
    tech: ["TypeScript", "React", "WebSocket", "Redis"]
    url: "https://hjalp.xyz"

contact:
  email: "[email protected]"
  github: "https://github.com/AB-70"
  linkedin: "https://linkedin.com/in/abdul70"
  website: "https://sattar.dev"
  twitter: "https://twitter.com/i0x46"

Loading Configuration

Load Function

config/config.go:63-73
func Load(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    var cfg Config
    if err := yaml.Unmarshal(data, &cfg); err != nil {
        return nil, err
    }
    return &cfg, nil
}
Usage in main.go:
main.go:31-34
cfg, err := config.Load("config.yaml")
if err != nil {
    log.Fatalf("Failed to load config: %v", err)
}

Validation Rules

The application does not perform schema validation. Invalid YAML will cause startup failure.
Best practices:
  • Ensure all required fields are present
  • Use valid YAML syntax (check indentation)
  • Test with go run . config.yaml before deployment
  • Keep ASCII art width under 80 characters for terminal compatibility

Default Values

FieldDefaultNotes
SkillItem.level0When using string syntax
All Contact fields""Empty strings are valid; UI hides empty fields

Configuration in Production

The config file must be in the same directory as the executable or specify a path:
# Default: looks for config.yaml in current directory
./ssh-portfolio

# Custom path (requires code change)
config.Load("/path/to/config.yaml")
Store config.yaml in version control but keep SSH host keys (.ssh/id_ed25519) in .gitignore.

Type Reference

Go to YAML Type Mapping

Go TypeYAML TypeExample
stringString or block scalar"text" or |
intInteger5
[]stringSequence["a", "b"]
[]SkillItemSequence of strings or objectsMixed syntax allowed
[]SkillCategorySequence of objects- category: ...

Expandable Nested Structures

Build docs developers (and LLMs) love