diff --git a/package.json b/package.json index fa4eb5f..c6cbcff 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "start": "node --openssl-legacy-provider build/dev-server.js", "dev:dobkin": "RESUME_NAME=dobkin node --openssl-legacy-provider build/dev-server.js", "dev:cherepaha": "RESUME_NAME=cherepaha node --openssl-legacy-provider build/dev-server.js", + "dev:levit": "RESUME_NAME=levit node --openssl-legacy-provider build/dev-server.js", "pdf": "node scripts/export.js", "pdf:green": "node scripts/export.js green", "pdf:ai-bw": "node scripts/export.js ai-bw", @@ -33,6 +34,8 @@ "export:cherepaha:ai-bw": "RESUME_NAME=cherepaha concurrently \"npm run dev\" \"node scripts/export.js ai-bw\" --success first --kill-others --raw", "export:bw": "node scripts/export-cli.js", "export:cherepaha": "RESUME_NAME=cherepaha concurrently \"npm run dev\" \"npm run pdf\" --success first --kill-others --raw", + "export:levit": "RESUME_NAME=levit concurrently \"npm run dev\" \"npm run pdf\" --success first --kill-others --raw", + "export:levit:ai-bw": "RESUME_NAME=levit concurrently \"npm run dev\" \"node scripts/export.js ai-bw\" --success first --kill-others --raw", "lint": "eslint --ext .js,.vue src scripts", "lint:fix": "eslint --ext .js,.vue src scripts --fix" }, diff --git a/resume/dobkin.yml b/resume/dobkin.yml index 6c05d06..8d650b5 100644 --- a/resume/dobkin.yml +++ b/resume/dobkin.yml @@ -2,165 +2,169 @@ # Experience: timeperiod = dates only; location = separate line. description: | = one bullet per line. # Optional: remote: true | false | hybrid; employment: contract | full-time | co-op (icons + legend on green/purple). # Optional contact: github_profile = public GitHub.com username (add if you use both Forge + GitHub). +# +# ───────────────────────────────────────────────────────────────────────────── +# STACK TOGGLE +# Flip show_stack below to hide/show ALL per-role "Stack:" lines at once. +# Set to false to hide all Stack: lines across the resume. Default: true (show). +# ───────────────────────────────────────────────────────────────────────────── +show_stack: true name: first: ILIA middle: last: DOBKIN -about: "Senior Software Development Engineer in Test with 20+ years across audit/financial platforms and modern regulated web, including real-money iGaming. Experienced owning Playwright/Cypress/Selenium automation across UI, API, and performance, plus CI/CD pipelines and DevOps tooling. Known for test-pyramid and shift-left practices, stabilizing flaky suites, and improving pipeline reliability in high-availability environments. Proactively eliminates repetitive work across teams through scripting, scheduled jobs, and workflow automation—freeing colleagues to focus on higher-value tasks. Improved end-to-end regression cycle time by ~40% by expanding automated coverage and parallelizing suites in CI pipelines." -core_strengths: "" +about: "Senior SDET with 20+ years in audit/financial software and regulated web, including real-money iGaming. Deep across Playwright, Swagger/OpenAPI contract testing, and performance baselines integrated into CI/CD. I treat automation as a personal discipline as much as a job: scripts, shortcuts, and agents that streamline my day so engineering effort goes where it matters. Strong instinct for stabilizing flaky suites, tightening quality gates, and removing manual regression effort wherever it lives." position: Senior Software Development Engineer in Test (SDET) # Shown next to the map pin in the header (green / ai-bw). Other themes use it as a location line; leave year empty to show "Based in …" instead of "Born … in …". +work_auth: "Canadian citizen" + birth: year: - location: Toronto, Ontario, Canada + location: "Remote (ET)" experience: -- company: Niyasoft Canada Inc. +- company: Niyasoft position: Senior Quality Assurance Automation Engineer - timeperiod: August 2023 - April 2026 + timeperiod: August 2023 – April 2026 remote: true employment: full-time location: Vaughan, Ontario, Canada + stack: "Playwright, TypeScript, GitHub Actions, PostgreSQL, GCP" description: | - • Built and maintained 300+ Playwright E2E tests and 250+ API/integration tests (Swagger/OpenAPI) plus Artillery performance suites for a regulated online-casino platform; coverage spanned happy-path, negative, workflow, page-navigation, and network request/response checks across payments, wallet/cashier, game/lobby, and back-office flows—cutting manual regression effort by ~50% and catching regressions earlier in the pipeline. - • Stabilized the Playwright suite by replacing brittle waits with deterministic patterns and improving environment readiness, cutting flaky-test noise and keeping daily pass rates consistently above ~95% across parallel CI stages. - • Validated responsible gaming and player protection end-to-end—deposit/loss/session limits, self-exclusion, cooling-off, reality checks—supporting successful compliance posture across licensed wagering markets. - • Ran compliance-sensitive scenarios for geo-eligibility, age-gating, and restricted jurisdictions with audit-friendly logging; traceability artifacts available for licensing reviews on demand. - • Optimized GitHub Actions pipelines (regression, functional, component, smoke) with parallelized stages and daily PR/review cadence, keeping feedback time short on a high-availability real-money stack. - • Monitored API reliability via GCP, Prometheus, and alerting; validated PostgreSQL-backed data integrity and prevented sev-1 incidents by catching performance regressions with Artillery baselines before release. + • Built and maintained **300+ Playwright E2E tests** and 250+ API/integration tests plus performance suites for a regulated online-casino platform; coverage spanned happy-path, negative, workflow, page-navigation, and network request/response checks across payments, wallet/cashier, game/lobby, and back-office flows—cutting manual regression effort by 50% and catching regressions earlier in the pipeline. + • Stabilized the **Playwright suite** by replacing brittle waits with deterministic patterns and improving environment readiness; reduced flaky-test noise and maintained daily pass rates above 90% across parallel CI stages. + • Validated **responsible gaming and player protection** end-to-end—deposit/loss/session limits, self-exclusion, cooling-off, reality checks—supporting successful compliance posture across licensed wagering markets. + • Ran compliance-sensitive scenarios for **geo-eligibility** with audit-friendly logging; traceability artifacts available for licensing reviews on demand. + • Optimized **GitHub Actions pipelines** (regression, functional, component, smoke) with parallelized stages and daily PR/review cadence, keeping feedback time short on a high-availability real-money stack. + • Monitored GCP metrics and alerts for API reliability; validated **PostgreSQL-backed data integrity** and prevented sev-1 incidents by catching performance regressions before release. + • Authored and enforced **Swagger/OpenAPI contract tests** against backend microservices, catching breaking schema changes before they reached downstream consumers. - company: RIOS Canada position: Software Development Engineer in Test (SDET) - timeperiod: June 2022 - July 2023 + timeperiod: June 2022 – July 2023 remote: true employment: contract location: Toronto, Ontario, Canada + stack: "Cypress, JavaScript, Bitbucket CI, Swagger/OpenAPI, Ansible" description: | - • Built Cypress E2E and API suites (Swagger/OpenAPI) from scratch across core product flows; owned patterns, fixtures, and stability, enabling every-commit checks and cutting manual regression time by ~40% per release. - • Introduced AODA/WCAG accessibility checks (alt text, keyboard nav, contrast) into Bitbucket CI gates, preventing accessibility regressions across web and mobile releases. - • Automated test-environment provisioning with Ansible, producing disposable, repeatable setups that shortened spin-up time and eliminated pre-regression drift. - • Partnered with engineering and product on defect triage, risk-based prioritization, and pragmatic quality gates without blocking incremental delivery. + • Built **Cypress E2E and API suites** (Swagger/OpenAPI) from scratch across core product flows, including shared test data builders and fixture libraries—enabling every-commit CI checks and cutting manual regression time by 40% per release. + • Introduced **AODA/WCAG accessibility checks** (alt text, keyboard nav, contrast) into Bitbucket CI gates, preventing accessibility regressions across web and mobile releases. + • Automated test-environment provisioning with **Ansible**, producing disposable, repeatable setups that shortened spin-up time and eliminated pre-regression drift. + • Partnered with engineering and product on **defect triage**, risk-based prioritization, and pragmatic quality gates without blocking incremental delivery. - company: Attabotics position: QA Automation Developer - timeperiod: September 2021 - May 2022 + timeperiod: September 2021 – May 2022 remote: true employment: contract location: Calgary, Alberta, Canada + stack: "SpecFlow, Gherkin, C#, .NET, Azure, Docker, SQL Server" description: | - • Maintained 3,500+ SpecFlow/Gherkin scenarios with C# in .NET/Azure, owning flaky-test triage and keeping daily build stability above ~90% across the suite. - • Practiced left-shift QA in a large Agile team: co-authored scenarios with developers early in sprint, tightened Given/When/Then clarity, and shortened feedback from story to green build. - • Stood up Docker-based local and CI-aligned test environments; used SQL Server for data setup, assertions, and traceability across integrated warehouse-automation workflows. + • **Mentored developers** on **testable design** and BDD best practices, improving scenario quality and reducing review churn on test PRs. + • Maintained **3,500+ SpecFlow/Gherkin scenarios** with C# in .NET/Azure, owning flaky-test triage and keeping daily build stability above 90% across the suite. + • Caught defects earlier and tightened **Given/When/Then** clarity by co-authoring scenarios with developers early in the sprint, shortening feedback from story to green build in a large Agile team (**left-shift QA**). + • Stood up **Docker-based local and CI-aligned test environments**; used SQL Server for data setup, assertions, and traceability across integrated warehouse-automation workflows. -- company: Levkin Inc. +- company: Levkin position: Senior Software Developer - timeperiod: October 2020 - August 2021 + timeperiod: October 2020 – August 2021 remote: true employment: contract location: Vaughan, Ontario, Canada + stack: "Playwright, GitLab CI/CD, Terraform, AWS S3, Grafana" description: | - • Built reusable Playwright building blocks with deterministic patterns, eliminating arbitrary waits and measurably reducing suite flakiness. + • Built reusable **Playwright** patterns that replaced arbitrary waits, measurably reducing suite flakiness and stabilizing CI runs. • Audited and refactored legacy test and UI code; documented testing strategy and shared patterns across the team. - • Optimized GitLab CI/CD pipelines for speed and reliability; piped test and pipeline metrics into Grafana dashboards for release visibility. - • Provisioned AWS (S3) environments with Terraform, validated end-to-end, and promoted to dev via the team's release procedure. - • Ongoing: self-hosted infrastructure lab and local-GPU AI assistant under the Levkin brand—see Projects section. + • Optimized **GitLab CI/CD** pipelines for speed and reliability; piped test and pipeline metrics into Grafana dashboards for release visibility. + • Provisioned AWS (S3) environments with **Terraform**, validated end-to-end, and promoted to dev via the team's release procedure. + • Introduced **page object patterns and shared utility layer** that cut new-test authoring time and improved cross-team consistency. + • Ongoing: self-hosted infrastructure lab and local-GPU AI projects—see Projects section. -- company: Accountants Templates Inc. - position: Senior Software Developer - timeperiod: August 2019 - August 2020 - remote: true - employment: contract - location: Calgary, Alberta, Canada - description: | - • Owned CaseWare/CaseView template delivery including compliance updates, standards-driven releases, and documentation for internal and client use. - • Reviewed software for improvements and implemented recommendations and collaborated with support on reported issues. - • Automated build and packaging workflows with scripting, compressing roughly eight hours of manual release effort down to under two minutes per cycle. +- company: EARLIER CAREER + timeperiod: 2009 – 2020 -- company: MNP LLP - position: Senior Application Developer 2 - timeperiod: August 2017 - June 2019 - remote: true - employment: full-time - location: Toronto, Ontario, Canada - description: | - • Developed and maintained C# / .NET / .NET Core applications integrating with CaseWare/CaseView; extended with JavaScript where specs required. - • Contributed automation strategy and hands-on Selenium/Cucumber work; managed Jenkins triggers, Cucumber reporting, and Azure DevOps pipelines. - -- company: CaseWare International Inc. - position: Software Developer - timeperiod: August 2006 - June 2017 +- company: CaseWare International remote: hybrid employment: full-time - location: Toronto, Ontario, Canada + stack: "C#, .NET, SQL Server, SilkTest, Agile/Scrum" description: | - • Delivered features, defect fixes, and client templates (JavaScript, HTML, YUI, jQuery, CSS) for global financial/audit systems; automated validation with SilkTest. - • Mentored junior developers on conventions, debugging, and code review; built reusable JS libraries; Agile Scrum, Jira, Git. + 11 years of feature development, client templates, and SilkTest automation for global audit/financial systems; mentored juniors. + +- company: MNP + remote: true + employment: full-time + stack: "C#, .NET Core, Selenium, Cucumber, Jenkins, Azure DevOps" + description: | + .NET development on CaseWare/CaseView with Selenium/Cucumber automation across Jenkins and Azure DevOps pipelines. + +- company: Accountants Templates + remote: true + employment: contract + stack: "CaseWare/CaseView, build automation, scripting" + description: | + CaseWare/CaseView template delivery with build/packaging scripts that cut release effort from ~8 hours to under 2 minutes. - company: ROLI Consulting - position: Web/Application Developer - timeperiod: January 2001 - July 2012 remote: true - employment: part-time - location: Vaughan, Ontario, Canada + employment: contract + stack: "Python, Twilio API, multi-stack web" description: | - • Voice broadcasting and SMS service (Python, Twilio API); websites across multiple stacks; technical consulting for nonprofits and SMBs. - -- company: Earlier Career - timeperiod: May 2005 - August 2006 - sub_companies: - - name: Kaboose Inc. - remote: false - employment: contract - - name: Coutts Information Services - remote: false - employment: full-time - - name: EDS / Scotiabank - remote: false - employment: co-op - description: | - • QA automation with QTP/Quality Center, cross-browser and end-to-end testing, and UAT support (Kaboose); Java/J2EE development (Coutts); Informatica ETL co-op on AIX/DB2 (EDS/Scotiabank). + Voice/SMS broadcasting service plus multi-stack web and technical consulting for nonprofits and SMBs. education: [] # Skills: grouped, deduped, ATS-friendly. Two-column grid in green template. skills: -- name: "Test automation: Playwright, Cypress, Selenium, SilkTest; UI, API, mobile, cross-browser; page object model, BDD" +- name: "**Test automation**: Playwright, Cypress, Selenium, SilkTest; UI, API, mobile, cross-browser; page object model, BDD" level: 96 -- name: "Languages & frameworks: TypeScript, JavaScript, C#, .NET, Python, Java, Bash/Shell, Node.js, ASP.NET, Spring Boot, HTML/CSS" +- name: "**Domains**: regulated iGaming (real-money), audit & financial software, warehouse automation, accessibility-compliant web (AODA/WCAG)" + level: 88 +- name: "**Languages & frameworks**: TypeScript, JavaScript, C#, .NET, Python, Java, Bash/Shell, Node.js, ASP.NET, Spring Boot, HTML/CSS" level: 92 -- name: "CI/CD & DevOps: GitHub Actions, GitLab, Bitbucket, Jenkins, Azure DevOps; Git, Terraform, Ansible, Docker, SonarQube, self-hosted runners" +- name: "**CI/CD & DevOps**: GitHub Actions, GitLab, Bitbucket, Jenkins, Azure DevOps; Git, Terraform, Ansible, Docker, SonarQube, self-hosted runners" level: 92 -- name: "Cloud & infra: AWS (Lambda, S3), Azure, GCP; Linux administration, Proxmox, Caddy, TrueNAS, Vaultwarden" +- name: "**Cloud & infra**: AWS (Lambda, S3), Azure, GCP; Linux administration, Proxmox, Caddy, TrueNAS, Vaultwarden" level: 84 -- name: "Observability & performance: Grafana, Prometheus, Sentry, DataDog, Artillery, k6, JMeter, metrics & logging" +- name: "**Observability & performance**: Grafana, Prometheus, Sentry, DataDog, Artillery, k6, JMeter, metrics & logging" level: 86 -- name: "Data & domain: PostgreSQL, SQL Server, MySQL, DB2, Informatica/ETL; CaseWare/CaseView, audit & financial software" +- name: "**Data & domain**: PostgreSQL, SQL Server, MySQL, DB2, Informatica/ETL" level: 78 -- name: "QA practices: BDD (SpecFlow, Cucumber, Gherkin), API testing (Postman, Swagger/OpenAPI), accessibility (AODA/WCAG), risk-based testing and prioritization, defect triage, quality gates, flaky-suite stabilization, test data and fixtures, Agile/Scrum, Jira, shift-left QA" +- name: "**QA testing types**: unit, integration, regression, smoke, exploratory, load, stress, end-to-end; API testing (Postman); accessibility (AODA/WCAG)" level: 90 -- name: "Leadership & collaboration: mentoring developers, test strategy and documentation, partnering with product and engineering on quality planning, release risk, and pragmatic gates without blocking delivery" - level: 84 -- name: "AI & LLM tooling: AI-assisted engineering with Cursor, GitHub Copilot, Perplexity, Claude Code, ChatGPT, Windsurf, and Amazon Q Developer; GenAI-assisted test design and refactors; prompt and workflow validation; privacy-first local LLM usage; MCP servers and agent-based automation" +- name: "**QA process**: BDD, risk-based prioritization, defect triage, quality gates, flaky-suite stabilization, shift-left QA, Agile/Scrum, Jira" + level: 88 +- name: "**AI & LLM tooling**: AI-assisted engineering with Cursor and Claude Code; privacy-first local LLM usage; MCP servers and agent-based automation; GenAI-assisted test design and refactors." level: 82 knowledge: projects: -- name: Levkin — Self-Hosted Infrastructure Lab +- name: "Self-Hosted Infrastructure Lab" + stack: "Proxmox, Ansible, Caddy, TrueNAS, Gitea, SonarQube" description: | - • Proxmox-based homelab (VMs/LXC): Gitea + CI runners, Vaultwarden, Vikunja, Uptime Kuma, Mailcow, Listmonk, n8n, SonarQube—provisioned via Ansible, Caddy edge TLS, TrueNAS backups. - • Full Linux admin: multi-domain DNS, firewall hardening, monitoring, and repeatable deploys; patterns from this lab directly informed production DevOps decisions in later roles. + • Proxmox homelab (VMs/LXC) with Gitea, CI runners, Vaultwarden, Uptime Kuma, Mailcow, SonarQube—**Ansible**-provisioned, Caddy TLS, TrueNAS backups; patterns directly informed production DevOps decisions. -- name: Levkin — Privacy-First Local AI Assistant +- name: "sdetProfile — Portfolio as Playwright Report" + stack: "HTML, CSS, vanilla JS, Playwright (tests), ESLint, Stylelint" description: | - • Tool-using assistant wired into mail and calendars (triage, drafts, scheduling) on local GPU inference—prompts and context stay on-LAN, no SaaS LLMs. - • Composable with homelab identity, TLS, secrets, and automation so event-driven workflows hand off without third-party model APIs; used daily for personal productivity. + • Zero-framework personal portfolio styled as a **Playwright** test runner—sidebar explorer, editor tabs, trace/network/source panels, tag filtering, keyboard shortcuts, and theme cycling (dark/light/WCAG AAA); 37 real Playwright specs verify the live site. -- name: Levkin — Playwright MCP server +- name: "Atlas — Local Voice Agent" + stack: "Python, MCP, ASR/TTS, local LLM (RTX), Playwright" description: | - • Built an MCP server for developers to use from Cursor and other MCP-capable assistants while writing Playwright tests—surfacing selectors, fixtures, and in-repo conventions so generated specs stay aligned with team patterns. + • Privacy-focused home voice assistant with on-device AI transcription and tool use—**Python**, local GPU inference, no third-party model APIs; wired into calendar and home automation. + +- name: "AtAnyRate — Event-Driven Pricing" + stack: "Python, Playwright, Telegram Bot API, Ticketmaster/SeatGeek APIs, Docker" + description: | + • **Python** app that identifies Toronto events likely to spike Airbnb demand, sends Telegram alerts, and optionally adjusts nightly prices via **Playwright** browser automation. + +- name: "LLM Council — Multi-Model Chat UI" + stack: "Python, FastAPI, httpx, React, Vite, Ollama/vLLM" + description: | + • Local web UI that fans each prompt to multiple LLMs and presents side-by-side responses—**Python** backend, diverse model voting for higher-confidence answers. hobbies: [] @@ -168,9 +172,9 @@ contributions: [] contact: email: idobkin@gmail.com - phone: +1 (647) 987-2792 + phone: street: - city: Toronto, Ontario, Canada + city: website: https://www.linkedin.com/in/idobkin/ website_label: LinkedIn github: https://git.levkin.ca diff --git a/resume/levit.yml b/resume/levit.yml new file mode 100644 index 0000000..1a52bdd --- /dev/null +++ b/resume/levit.yml @@ -0,0 +1,144 @@ +/* #*/ export const PERSON = ` +# experience: timeperiod = dates; location = separate line; description: | = one bullet per line. +# Optional: remote: true | false | hybrid; employment: contract | full-time | co-op +# Build: RESUME_NAME=levit npm run dev | PDF: RESUME_NAME=levit npm run export + +name: + first: BORIS + middle: + last: LEVIT + +position: Enterprise / Security / Data / AI Architect (CISO Track) + +about: "CISSP-certified Enterprise Security Architect and DevSecOps/MLSecOps leader with 20+ years delivering security modernization for public sector and regulated enterprises. Designs and operationalizes zero-trust and cloud-native security platforms across AWS/Azure/GCP, integrating SIEM/SOAR, EDR/XDR, identity governance/PAM, and data/AI security. Strong track record producing actionable TRAs/PIAs, executive-ready roadmaps, and operating models that connect NIST/ISO frameworks to measurable controls, telemetry, and incident readiness." + +core_strengths: "Zero Trust & SSE/SASE; cloud security architecture; SIEM/SOAR modernization; DevSecOps & secure CI/CD; IAM/PAM & IGA; AI/LLM security & governance; threat modeling & risk assessments; public-sector security standards." + +birth: + year: + location: Toronto, Ontario, Canada + +experience: +- company: InTunnel Monitor + position: Enterprise / Security / Data / AI Architect, DevSecOps Lead + timeperiod: September 2017 - Current + remote: hybrid + employment: contract + location: Toronto, ON, Canada + description: | + • Led architecture and delivery for cybersecurity modernization engagements across federal/provincial clients and regulated enterprises (including OSFI, GC DND, and Ontario Securities Commission). + • Designed and operationalized zero-trust and SSE/SASE solutions across hybrid and multi-cloud (AWS/Azure/GCP), including ZTNA, CASB, SWG, FWaaS, and SaaS posture management patterns. + • Modernized SecOps by integrating SIEM + SOAR with EDR/XDR/DLP and threat intelligence (ArcSight/Splunk/Sentinel + SOAR patterns), enabling automated playbooks for phishing, identity events, and vulnerability response. + • Delivered TRAs/PIAs, security roadmaps, and capability assessments aligned to NIST CSF v2, CIS Controls v8, ISO 27001/27701, ITSG-33, and MITRE ATT&CK; advised on data residency, privacy-by-design, and GRC. + • Implemented DevSecOps and continuous compliance practices: secure CI/CD, IaC (Terraform/CloudFormation), container/Kubernetes security, SAST/DAST/SCA, and security KPI/telemetry reporting. + • Architected AI/LLM security posture and delivery approach: AI footprint mapping, AI-specific threat modeling, guardrails/safety, governance, and observability—supporting secure RAG and agentic patterns and aligning to NIST AI RMF. + • Designed data security and governance for modern analytics platforms (Purview, Databricks, Snowflake/BigQuery patterns), including cataloging, access control, and sensitive-data handling across pipelines. + • Delivered IAM/PAM and identity governance work (CyberArk, Okta/Entra ID patterns, access reviews), supporting least privilege and privileged session controls. + • Supported co-managed SOC / MSSP transitions by defining operating models, SLAs, metrics, reporting, and Tier-3 incident investigation processes. + • Communicated risk and architecture decisions to executives and technical teams via briefings, risk registers, and hands-on enablement workshops. + +- company: HPE / DXC (SOC / MSSP) + position: Security Incident Analyst (Tier 2/3) + timeperiod: June 2015 - August 2017 + remote: false + employment: full-time + location: Toronto, ON, Canada + description: | + • Led incident triage, investigation, and remediation across financial and public-sector environments using ArcSight and network/app security controls (IDS/WAF). + • Developed SIEM content (queries, dashboards, correlation use cases) and incident playbooks; delivered RCA reporting and executive recommendations. + • Performed threat hunting and forensics (pcap analysis, IOC enrichment, CTI workflows) and automated analysis tasks with scripting. + +- company: Metsuke + position: Security Consultant / Architect + timeperiod: February 2012 - May 2015 + remote: hybrid + employment: contract + location: Toronto, ON, Canada + description: | + • Delivered security architecture, IAM/PAM remediation, and risk/vulnerability assessments for enterprise and public-sector clients. + • Implemented secure CI/CD practices and integration hardening across infrastructure and applications; produced TRAs/PIAs and compliance-aligned documentation. + • Designed and supported SIEM/IAM programs and logging/monitoring improvements for operational resilience; authored SOPs and technical documentation for SOC/hosting teams. + +- company: TD Bank + position: Senior Security Specialist + timeperiod: August 2010 - September 2011 + remote: false + employment: full-time + location: Toronto, ON, Canada + description: | + • Led remediation after SOX/PCI audits across access control, privileged access, logging/monitoring, and secure system design for legacy enterprise platforms. + • Supported SIEM deployment and compliance reporting; improved audit trails and incident readiness across on-prem environments. + • Partnered with audit, infrastructure, and application teams to translate requirements into sustainable controls and operating procedures. + +- company: Earlier Career (Selected) + position: Security Architect / Developer / Manager (multiple roles) + timeperiod: 1998 - 2010 + description: | + • Security architecture, consulting, and leadership roles across ISPs, financial services, and technology firms (including Research In Motion/BlackBerry, Q1 Labs/IBM QRadar early work, N-Dimension SCADA security integration, and others). + • Led/owned projects spanning incident response, vulnerability assessment, secure platform engineering, IAM, and governance artifacts aligned with ISO/NIST/OWASP and regulated environments. + • IT/OT and SCADA security integration work (Modbus/DNP3, ruggedized platforms, secure gateways, and monitoring) supporting critical infrastructure and utility contexts. + +education: +- degree: MS Diploma (evaluated by York University, 2002) + description: Moscow Institute of Electronic Techniques + +certifications: +- name: CISSP +- name: "Cloud Security Alliance — Agentic AI Security Summit (2025)" +- name: "Cloud Security Alliance — Virtual Cloud Non-Human Identities (NHI) Summit (2025)" +- name: "LangChain Academy — Introduction to LangGraph (2025)" +- name: "DND/CAF Architecture Framework (DNDAF) & QualiWare EA Toolset (2023)" +- name: "ISC2 — Incident Management: Preparation and Response (2020)" +- name: "ISC2 — DevSecOps: Integrating Security into DevOps (2018)" +- name: "ArcSight ESM / SmartConnector Foundations & Administration (2016)" +- name: "Ongoing professional training (LinkedIn Learning; see LinkedIn profile for full list)" + +skills: +- name: "Security architecture: Zero Trust, SSE/SASE, network & endpoint security, cloud-native security, security reference architectures" + level: 96 +- name: "Cloud platforms: AWS, Azure, GCP (IAM, network segmentation, logging, workload protection, KMS/secrets, cloud posture management)" + level: 92 +- name: "SecOps & tooling: SIEM/SOAR, detection engineering, incident response, threat hunting, TI integration (ArcSight, Splunk, Sentinel/KQL, QRadar, Elastic, LogRhythm, Datadog; Cortex/XSIAM)" + level: 94 +- name: "DevSecOps & security testing: secure CI/CD (Jenkins/GitLab/GitHub), IaC (Terraform/CloudFormation), Kubernetes/Docker security, SAST/DAST/SCA, SBOM, WAF/API testing (Burp/Postman/Nmap/Nessus/Qualys/gotestwaf), policy-as-code (OPA)" + level: 92 +- name: "Identity & privileged access: IGA, RBAC/ABAC, SSO (SAML/OIDC/OAuth2), directory services (AD/LDAP), PAM (CyberArk), access reviews and lifecycle controls" + level: 90 +- name: "Risk, privacy & compliance: NIST CSF/800-53, CIS Controls, ISO 27001/27701/42001, ITSG-33, STRIDE/PASTA, MAESTRO, MITRE ATT&CK/ATLAS/D3FEND, privacy-by-design, TRA/PIA" + level: 92 +- name: "AI / LLM security: NIST AI RMF, OWASP Top 10 for LLM/Agentic AI, secure RAG/agentic patterns, model/data protection, guardrails, evals/observability, jailbreak prevention and red teaming" + level: 88 +- name: "AI governance & regulation: EU AI Act, Bill C-27, GDPR; model risk management, data residency, and compliance-by-design for AI/data platforms" + level: 84 +- name: "Enterprise architecture: TOGAF, SABSA, Zachman; public-sector architecture practices (DNDAF familiarity) and traceable architecture decisions" + level: 82 +- name: "Data & platforms: Snowflake, Databricks, Microsoft Purview, BigQuery, Synapse/Fabric patterns; lakehouse/medallion concepts; governance, cataloging, and access control" + level: 82 +- name: "Programming & automation: Python, Bash, Go; JSON/YAML; scripting for log processing, reporting, and security telemetry" + level: 82 + +projects: +- name: "Selected Case Studies (SOC / SecOps / Architecture)" + description: | + • Web-attack “false positive” triage (banking): validated “blocked” actions end-to-end across ArcSight + security controls, performed packet/header analysis to attribute traffic to an authorized third-party test; closed ticket with evidence and trained analysts to repeat the method. + • Web-attack “true positive” validation (banking): confirmed distributed hostile sources, verified exploit applicability against the client’s patched stack, and supported containment by identifying/closing an exposed infrastructure path while documenting risk and residual exposure. + • SSH brute-force investigation: traced an internal-source alert to a perimeter exposure by resolving NAT/logging ambiguity; escalated with clear evidence and drove closure of an internet-exposed SSH tunnel through stakeholder follow-ups. + • Detection engineering QA: diagnosed missing alerts to a subtle filter mismatch (leading whitespace) by comparing connector/base-event fields to use-case logic; handed off a precise fix to the content engineering team. + • Critical-infrastructure signature validation (SCADA/Modbus): recreated malicious traffic in a lab, identified errors in 3 IDS signatures that the industry had accepted, corrected them, and reported upstream—improving detection accuracy and establishing a repeatable validation approach. + +knowledge: [] +hobbies: [] +contributions: [] + +contact: + email: boris.levit@yahoo.com + phone: +1 (416) 804-7520 + street: + city: Toronto, ON M2R 3N8, Canada + website: "https://www.linkedin.com/in/boris-levit-025a88" + website_label: LinkedIn + github: "" + github_label: "" + +lang: en +` diff --git a/src/resumes/ai-bw.vue b/src/resumes/ai-bw.vue index d5a1884..1aedafe 100644 --- a/src/resumes/ai-bw.vue +++ b/src/resumes/ai-bw.vue @@ -1,18 +1,22 @@ + @@ -116,23 +168,21 @@

{{ lang.projects }}

-

{{ project.name }}

- + class="compact-description">

{{ lang.education }}

-

{{education.description}}

-

{{education.degree}} | {{education.timeperiod}}

+

+

| {{education.timeperiod}}

@@ -142,7 +192,7 @@
  • {{ certificationLabel(c) }}
  • + class="list-item-black">
    @@ -155,10 +205,19 @@
    {{ skill.name }}
    + class="skill-cell"> + + +
    + @@ -169,7 +228,78 @@ import { getVueOptions } from './options'; const name = 'ai-bw'; const baseOptions = getVueOptions(name); export default Vue.component(name, Object.assign({}, baseOptions, { + props: { + bodyFont: { + type: String, + default: '' + }, + rootId: { + type: String, + default: 'template' + } + }, + computed: Object.assign({}, baseOptions.computed || {}, { + bodyFontStyle () { + if (!this.bodyFont) return {}; + return { fontFamily: this.bodyFont }; + } + }), methods: Object.assign({}, baseOptions.methods || {}, { + headerInfoItems () { + const items = []; + const push = (key, icon, text, href) => { + const t = (text === undefined || text === null) ? '' : String(text).trim(); + if (!t) return; + items.push({ + key, + icon, + text: t, + href: href || '' + }); + }; + + const c = (this.person && this.person.contact) ? this.person.contact : {}; + const links = this.contactLinks || {}; + + push('email', 'fa fa-envelope', c.email, links.email); + push('phone', 'fa fa-phone-square', c.phone, links.phone); + if (c.website && links.website) { + push('website', 'fa fa-linkedin', c.website_label || c.website, links.website); + } + if (c.github && links.github) { + push('github', 'fa fa-code-fork', c.github_label || 'Git', links.github); + } + if (c.github_profile && links.github_profile) { + const u = String(c.github_profile).trim().replace(/^@/, ''); + push('github_profile', 'fa fa-github', c.github_profile_label || ('@' + u), links.github_profile); + } + if (c.personal_site && links.personal_site) { + push('personal_site', 'fa fa-globe', c.personal_site_label || c.personal_site, links.personal_site); + } + if (this.person && this.person.birth && this.person.birth.location) { + push('location', 'fa fa-map-marker', this.person.birth.location, ''); + } + if (this.person && this.person.work_auth) { + push('work_auth', 'header-emoji-flag', this.person.work_auth, ''); + } + return items; + }, + escapeHtml (value) { + const s = (value === undefined || value === null) ? '' : String(value); + return s + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }, + renderBold (value) { + const base = this.escapeHtml(value); + if (!base) return ''; + let out = base.replace(/\*\*(.+?)\*\*/g, '$1'); + out = out.replace(/\*(.+?)\*/g, '$1'); + return out; + }, experienceLocation (exp) { const loc = exp && exp.location; return (loc !== undefined && loc !== null && String(loc).trim()) ? String(loc).trim() : ''; @@ -192,13 +322,35 @@ export default Vue.component(name, Object.assign({}, baseOptions, { return lines; }, stripLeadingBullet (line) { - return String(line).replace(/^[•*-]\s*/, '').trim(); + return String(line).replace(/^(?:[•-]|\*(?!\*))\s*/, '').trim(); }, certificationLabel (c) { if (c === undefined || c === null) return ''; if (typeof c === 'string') return String(c).trim(); const n = c.name; return (n !== undefined && n !== null && String(n).trim()) ? String(n).trim() : ''; + }, + skillParts (name) { + const raw = (name === undefined || name === null) ? '' : String(name); + const idx = raw.indexOf(':'); + if (idx <= 0) { + return { + label: '', + text: raw.trim() + }; + } + const label = raw.slice(0, idx).trim(); + const text = raw.slice(idx + 1).trim(); + if (!label || !text) { + return { + label: '', + text: raw.trim() + }; + } + return { + label, + text + }; } }) })); @@ -209,9 +361,14 @@ export default Vue.component(name, Object.assign({}, baseOptions, { @ink: #111; @muted: #444; @pad-x: 24px; -#template { +.ai-bw-tpl { box-sizing: border-box; - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; + /* Disable ligatures so PDF rendering doesn't drop ti/ff/fi glyphs + (was producing "So ware" / "le -shi QA" / "dra s" in print). */ + font-variant-ligatures: none; + font-feature-settings: "liga" 0, "clig" 0, "dlig" 0, "calt" 0; + text-rendering: optimizeLegibility; display: flex; flex-direction: column; min-height: 0; @@ -229,12 +386,12 @@ export default Vue.component(name, Object.assign({}, baseOptions, { p { margin: 0; - font-size: 11px; + font-size: 13px; } ul li { color: @ink; - font-size: 11px; + font-size: 13px; } a { @@ -251,6 +408,8 @@ export default Vue.component(name, Object.assign({}, baseOptions, { background: #fff; border-bottom: 2px solid @ink; padding: 14px @pad-x 10px; + position: relative; + z-index: 1; #header-left { width: 100%; @@ -263,7 +422,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { line-height: 1.15; } h2 { - font-size: 13px; + font-size: 14px; color: @muted; font-weight: 400; } @@ -271,7 +430,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { display: flex; flex-wrap: wrap; margin-top: 8px; - font-size: 10px; + font-size: 11px; gap: 4px 12px; span { @@ -280,40 +439,50 @@ export default Vue.component(name, Object.assign({}, baseOptions, { i { color: @ink; - font-size: 12px; + font-size: 13px; margin-right: 5px; vertical-align: middle; opacity: 0.9; } + .header-emoji-flag { + font-style: normal; + filter: grayscale(1) contrast(3); + display: inline-block; + vertical-align: middle; + line-height: 1; + &::before { + content: '🇨🇦'; + } + } } } } #resume-about { flex-shrink: 0; - padding: 6px @pad-x 8px; + padding: 6px @pad-x 4px; background: #fff; - border-bottom: 1px solid #ccc; + border-bottom: none; box-sizing: border-box; - h2 { - font-size: 12px; + #profile-title { + font-size: 15px; text-transform: uppercase; letter-spacing: 0.04em; - margin-bottom: 4px; + margin-bottom: 6px; font-weight: 700; } h2, p { color: @ink; } p { - font-size: 10px; + font-size: 12px; line-height: 1.38; margin: 0; white-space: pre-line; } .core-strengths { margin-top: 4px; - font-size: 10px; + font-size: 12px; line-height: 1.38; white-space: normal; } @@ -328,17 +497,20 @@ export default Vue.component(name, Object.assign({}, baseOptions, { min-width: 0; max-width: 100%; box-sizing: border-box; - padding: 8px @pad-x 8px; + padding: 6px @pad-x 6px; background: #fff; #experience-title, #education-title, #certifications-title, #skills-title, #projects-title { - font-size: 12px; + font-size: 15px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; } #experience-container { + border-top: 1px solid #999; + padding-top: 6px; + margin-top: 8px; padding-left: 0; padding-right: 0; box-sizing: border-box; @@ -352,7 +524,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { display: block; margin: 0 0 2px 0; padding: 0; - font-size: 9px; + font-size: 11px; line-height: 1.4; color: @muted; max-width: 100%; @@ -378,7 +550,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { i { display: inline-block; color: @ink; - font-size: 11px; + font-size: 13px; width: 1.25em; min-width: 1.25em; margin-right: 5px; @@ -393,25 +565,107 @@ export default Vue.component(name, Object.assign({}, baseOptions, { font-weight: 400; } + .experience-legend-text-icon { + display: inline-block; + font-weight: 700; + font-size: 13px; + color: @ink; + width: 1.25em; + min-width: 1.25em; + text-align: center; + margin-right: 5px; + vertical-align: baseline; + opacity: 0.9; + } + .experience { - margin: 0 0 9px 0; + margin: 0 0 7px 0; break-inside: avoid; page-break-inside: avoid; + & + .experience { + border-top: 1px solid #ddd; + padding-top: 6px; + margin-top: 6px; + } + &:first-child { + border-top: none; + } &:last-child { margin-bottom: 0; + border-top: none; + } + &.experience-section { + border-top: none; + padding-top: 8px; + margin-top: 10px; + margin-bottom: 0; + break-after: avoid; + page-break-after: avoid; + .spacer { + margin-bottom: 2px; + } + .job-description-list { + list-style-type: none; + padding-left: 0; + li { + padding-left: 0; + } + li:nth-child(even) { + margin-bottom: 5px; + } + li:last-child { + margin-bottom: 0; + } + } } ul { margin: 3px 0 0 0; } } + .experience.experience-compact { + margin: 0; + break-inside: avoid; + page-break-inside: avoid; + .company-row { + margin-bottom: 0; + line-height: 1.1; + } + } + .experience.experience-compact + .experience.experience-compact { + border-top: none; + padding-top: 0; + margin-top: 0; + } + .experience.experience-section + .experience.experience-compact { + border-top: none; + padding-top: 0; + margin-top: 0; + } + + .inline-stack { + font-size: 11px; + font-style: italic; + font-weight: 400; + color: @muted; + margin-left: 8px; + white-space: normal; + } + + .stack-brace { + font-weight: 700; + font-style: normal; + color: @ink; + opacity: 0.9; + } + /* Grid keeps location in the right column; flex-wrap was sending it to a full-width row under bullets */ .company-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(min-content, max-content); column-gap: 10px; align-items: baseline; - margin: 0 0 5px 0; + margin: 0 0 3px 0; line-height: 1.25; .company-primary { min-width: 0; @@ -421,7 +675,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } .company { display: inline; - font-size: 13px; + font-size: 14px; font-weight: 700; color: @ink; } @@ -445,7 +699,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } i { color: @ink; - font-size: 12px; + font-size: 13px; line-height: 1; vertical-align: baseline; opacity: 0.88; @@ -454,7 +708,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { .company-location { justify-self: end; align-self: baseline; - font-size: 10px; + font-size: 10.5px; font-weight: 400; color: @muted; line-height: 1.25; @@ -475,7 +729,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { grid-template-columns: minmax(0, 1fr) minmax(min-content, max-content); column-gap: 10px; align-items: baseline; - margin: 0 0 5px 0; + margin: 0 0 3px 0; line-height: 1.3; } @@ -485,9 +739,9 @@ export default Vue.component(name, Object.assign({}, baseOptions, { list-style-position: outside; list-style-type: disc; li { - font-size: 10px; - line-height: 1.32; - margin: 0 0 1px 0; + font-size: 11.5px; + line-height: 1.28; + margin: 0; padding-left: 1px; overflow-wrap: anywhere; word-break: break-word; @@ -497,8 +751,42 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } } - .job-title, .degree { + .stack-line { + margin: 0 0 3px 0; + font-size: 11.5px; + line-height: 1.3; + color: @muted; + font-style: italic; + white-space: normal; + overflow-wrap: anywhere; + word-break: normal; + } + + + .section-subheading { + font-size: 15px; + text-transform: uppercase; + letter-spacing: 0.04em; font-weight: 700; + margin: 0; + line-height: 1.25; + color: @ink; + } + + .section-subheading-date { + font-size: 11px; + font-weight: 400; + color: @muted; + text-transform: none; + letter-spacing: 0; + } + + .bullet-aside { + font-style: italic; + } + + .job-title { + font-weight: 500; color: @ink; font-size: 11px; min-width: 0; @@ -507,12 +795,22 @@ export default Vue.component(name, Object.assign({}, baseOptions, { word-break: break-word; } + .degree { + font-weight: 700; + color: @ink; + font-size: 12px; + min-width: 0; + max-width: 100%; + overflow-wrap: anywhere; + word-break: break-word; + } + .experience-timeperiod { justify-self: end; align-self: baseline; font-weight: 400; color: @muted; - font-size: 10px; + font-size: 10.5px; line-height: 1.3; text-align: right; white-space: nowrap; @@ -532,7 +830,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { .sub-company-name { font-weight: 700; color: @ink; - font-size: 11px; + font-size: 12px; } .sub-icon-gutter { @@ -545,7 +843,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { margin-right: 5px; i { color: @ink; - font-size: 11px; + font-size: 12px; line-height: 1; vertical-align: baseline; opacity: 0.88; @@ -563,7 +861,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { .education-timeperiod { font-weight: 400; color: @muted; - font-size: 11px; + font-size: 12px; } .education { @@ -571,7 +869,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } #skills-container { - margin-top: 14px; + margin-top: 16px; padding-left: 0; padding-right: 0; box-sizing: border-box; @@ -581,7 +879,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } #skill-description { - font-size: 9px; + font-size: 11px; line-height: 1.3; color: @muted; margin: 0 0 2px 0; @@ -594,8 +892,8 @@ export default Vue.component(name, Object.assign({}, baseOptions, { #skill-grid { display: grid; - grid-template-columns: 1fr; - column-gap: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: 14px; row-gap: 3px; margin: 0; padding: 0; @@ -604,7 +902,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } .skill-cell { - font-size: 9px; + font-size: 11.5px; line-height: 1.28; color: @ink; min-width: 0; @@ -617,8 +915,18 @@ export default Vue.component(name, Object.assign({}, baseOptions, { break-inside: avoid; } + .skill-label { + font-weight: 700; + color: @ink; + } + + .skill-text { + font-weight: 400; + color: @ink; + } + #projects-container { - margin-top: 14px; + margin-top: 16px; padding-left: 0; padding-right: 0; box-sizing: border-box; @@ -628,7 +936,18 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } .project { - margin: 0 0 4px 0; + margin: 0; + break-inside: avoid; + page-break-inside: avoid; + & + .project { + margin-top: 6px; + padding-top: 0; + border-top: none; + } + &:first-child { + margin-top: 0; + padding-top: 0; + } &:last-child { margin-bottom: 0; } @@ -637,21 +956,32 @@ export default Vue.component(name, Object.assign({}, baseOptions, { .company-section-label { font-style: italic; font-weight: 600; - font-size: 11px; + font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; color: @muted; } + .compact-description { + font-size: 11.5px; + line-height: 1.15; + color: @ink; + margin: 0 0 -1px 0; + padding-left: 1.1em; + overflow-wrap: anywhere; + word-break: break-word; + } + .project-name { - font-size: 12px; + font-size: 13px; font-weight: 700; color: @ink; - margin: 0 0 2px 0; + margin: 0; + line-height: 1.1; } #education-container { - margin-top: 14px; + margin-top: 16px; padding-left: 0; padding-right: 0; box-sizing: border-box; @@ -661,7 +991,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } #certifications-container { - margin-top: 14px; + margin-top: 16px; padding-left: 0; padding-right: 0; box-sizing: border-box; @@ -676,7 +1006,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { list-style-position: outside; list-style-type: disc; li { - font-size: 11px; + font-size: 12px; line-height: 1.4; margin: 0 0 2px 0; padding-left: 2px; @@ -691,7 +1021,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { } @page { - margin: 4mm 5mm 5mm 5mm; + margin: 7mm 5mm 5mm 5mm; } @page :first { @@ -702,7 +1032,7 @@ export default Vue.component(name, Object.assign({}, baseOptions, { .spacer { width: 100%; border-bottom: 1px solid #999; - margin: 4px 0 8px; + margin: 3px 0 6px; padding-top: 0; box-sizing: border-box; } diff --git a/src/resumes/options.js b/src/resumes/options.js index d329dcf..affa475 100755 --- a/src/resumes/options.js +++ b/src/resumes/options.js @@ -199,6 +199,12 @@ function getVueOptions (name) { key: 'coop', icon: 'fa fa-graduation-cap', label: 'Co-op' + }, + { + key: 'stack', + icon: '', + text: '{ }', + label: 'Stack' } ]; }