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
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"`
}
Your full name. Displayed in the portfolio.Example: "Abdul Sattar"
Your professional title or role.Example: "Sr. Full-stack Developer"
ASCII art displayed on the home page. Use YAML block scalar (|) for multi-line art.Example:ascii_art: |
███████╗ █████╗ ████████╗████████╗ █████╗ ██████╗
██╔════╝██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔══██╗
███████╗███████║ ██║ ██║ ███████║██████╔╝
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.
Array of skill categories. Each category contains related skills.See SkillCategory for structure.
Array of projects to showcase.See Project for structure.
Contact information and social links.See Contact for structure.
SkillCategory
type SkillCategory struct {
Category string `yaml:"category"`
Items []SkillItem `yaml:"items"`
}
Name of the skill category.Example: "Languages", "Frontend", "DevOps"
List of skills in this category.See SkillItem for structure.
SkillItem
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 of the skill.Example: "TypeScript", "Docker", "React"
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:
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
type Project struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Tech []string `yaml:"tech"`
URL string `yaml:"url"`
}
Project name.Example: "LogUI", "2ndLock"
Project description (1-2 sentences recommended).Example: "Real-time logging and application monitoring platform."
Array of technologies used in the project.Example: ["NodeJS", "Redis", "Clickhouse"]
Project URL or homepage.Example: "https://logui.xyz"
type Contact struct {
Email string `yaml:"email"`
GitHub string `yaml:"github"`
LinkedIn string `yaml:"linkedin"`
Website string `yaml:"website"`
Twitter string `yaml:"twitter"`
}
GitHub profile URL.Example: "https://github.com/AB-70"
LinkedIn profile URL.Example: "https://linkedin.com/in/abdul70"
Personal website URL.Example: "https://sattar.dev"
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:
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
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:
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
| Field | Default | Notes |
|---|
SkillItem.level | 0 | When 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 Type | YAML Type | Example |
|---|
string | String or block scalar | "text" or | |
int | Integer | 5 |
[]string | Sequence | ["a", "b"] |
[]SkillItem | Sequence of strings or objects | Mixed syntax allowed |
[]SkillCategory | Sequence of objects | - category: ... |
Expandable Nested Structures
Show Full Config Type Tree
Config
├── Name: string
├── Title: string
├── ASCIIArt: string
├── Intro: string
├── Skills: []SkillCategory
│ └── SkillCategory
│ ├── Category: string
│ └── Items: []SkillItem
│ └── SkillItem
│ ├── Name: string
│ └── Level: int
├── Projects: []Project
│ └── Project
│ ├── Name: string
│ ├── Description: string
│ ├── Tech: []string
│ └── URL: string
└── Contact
├── Email: string
├── GitHub: string
├── LinkedIn: string
├── Website: string
└── Twitter: string