commit 3fc75b636a277ffdb520dd41b46cdc550f5969a4 Author: Viktor Barzin Date: Thu May 7 23:13:25 2026 +0000 Initial commit — Hugo source, Dockerfile, Woodpecker pipeline Modernized kms.viktorbarzin.me reference page covering every Windows + Office Volume License GVLK Microsoft publishes, plus activation snippets, ODT config, and bootstrap script links. Co-Authored-By: Claude Opus 4.7 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c6da888 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.gitignore +.dockerignore +.woodpecker.yml +.woodpecker/ +README.md +public/ +resources/ +.hugo_build.lock +node_modules/ +*.log diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f16327f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +public/ +resources/ +.hugo_build.lock +node_modules/ +*.log diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..d04a998 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,39 @@ +when: + event: [push, manual] + +clone: + git: + image: woodpeckerci/plugin-git + settings: + attempts: 5 + backoff: 10s + +steps: + - name: build-and-push + image: woodpeckerci/plugin-docker-buildx + settings: + repo: + - forgejo.viktorbarzin.me/viktor/kms-website + logins: + - registry: forgejo.viktorbarzin.me + username: + from_secret: forgejo_user + password: + from_secret: forgejo_push_token + dockerfile: Dockerfile + context: . + auto_tag: true + platforms: + - linux/amd64 + tags: + - "latest" + - "${CI_COMMIT_SHA:0:8}" + + - name: deploy + image: bitnami/kubectl:latest + when: + branch: master + event: [push, manual] + commands: + - "kubectl set image deployment/kms-web-page kms-web-page=forgejo.viktorbarzin.me/viktor/kms-website:${CI_COMMIT_SHA:0:8} -n kms" + - "kubectl rollout status deployment/kms-web-page -n kms --timeout=300s" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..66946ff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1.6 +ARG HUGO_VERSION=0.139.0 +ARG NGINX_VERSION=1.27-alpine + +FROM klakegg/hugo:${HUGO_VERSION}-ext-alpine AS build +WORKDIR /src +COPY . . +RUN hugo --minify --gc --destination /out + +FROM nginx:${NGINX_VERSION} +LABEL org.opencontainers.image.source="https://forgejo.viktorbarzin.me/viktor/kms-website" \ + org.opencontainers.image.description="kms.viktorbarzin.me — KMS activation reference" + +# Strip default config so the bundled simple one stays +RUN rm -f /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /out /usr/share/nginx/html +EXPOSE 80 +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget -qO- http://127.0.0.1/ >/dev/null 2>&1 || exit 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5298c1 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# kms-website + +Source for [kms.viktorbarzin.me](https://kms.viktorbarzin.me) — a single-page reference for activating +Microsoft Volume License products against the home-lab KMS host (`kms.viktorbarzin.lan` / `kms.viktorbarzin.me`). + +## Stack + +- **Hugo** static site (one custom layout, single `_index`) +- **YAML data file** at `data/products.yaml` is the source of truth for all GVLK tables +- **nginx:alpine** Docker image (multi-stage build via Hugo) +- **Woodpecker CI** builds + pushes to `forgejo.viktorbarzin.me/viktor/kms-website` + and rolls the `kms-web-page` Deployment in the `kms` namespace +- **Terraform** in `infra/stacks/kms` consumes the image (`var.image_tag`) + +## Local dev + +```sh +hugo server -D +# → http://localhost:1313 +``` + +## Update GVLKs + +Edit `data/products.yaml`. Push. CI rebuilds and rolls. + +Sources of truth for keys: + +- Windows: +- Office / Project / Visio: diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..db0d14c --- /dev/null +++ b/content/_index.md @@ -0,0 +1,3 @@ +--- +title: "KMS Activation" +--- diff --git a/data/products.yaml b/data/products.yaml new file mode 100644 index 0000000..5f9614a --- /dev/null +++ b/data/products.yaml @@ -0,0 +1,568 @@ +windows: + # Windows 11 / Windows 10 — desktop SKUs (modern) + - family: "Windows 11 / 10" + edition: "Pro" + gvlk: "W269N-WFGWX-YVC9B-4J6C9-T83GX" + notes: "Same GVLK for Win11 and Win10 Pro." + current: true + - family: "Windows 11 / 10" + edition: "Pro N" + gvlk: "MH37W-N47XK-V7XM9-C7227-GCQG9" + notes: "EU 'N' edition (no media pack)." + current: true + - family: "Windows 11 / 10" + edition: "Pro for Workstations" + gvlk: "NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J" + current: true + - family: "Windows 11 / 10" + edition: "Pro for Workstations N" + gvlk: "9FNHH-K3HBT-3W4TD-6383H-6XYWF" + current: true + - family: "Windows 11 / 10" + edition: "Pro Education" + gvlk: "6TP4R-GNPTD-KYYHQ-7B7DP-J447Y" + current: true + - family: "Windows 11 / 10" + edition: "Pro Education N" + gvlk: "YVWGF-BXNMC-HTQYQ-CPQ99-66QFC" + current: true + - family: "Windows 11 / 10" + edition: "Education" + gvlk: "NW6C2-QMPVW-D7KKK-3GKT6-VCFB2" + current: true + - family: "Windows 11 / 10" + edition: "Education N" + gvlk: "2WH4N-8QGBV-H22JP-CT43Q-MDWWJ" + current: true + - family: "Windows 11 / 10" + edition: "Enterprise" + gvlk: "NPPR9-FWDCX-D2C8J-H872K-2YT43" + current: true + - family: "Windows 11 / 10" + edition: "Enterprise N" + gvlk: "DPH2V-TTNVB-4X9Q3-TJR4H-KHJW4" + current: true + - family: "Windows 11 / 10" + edition: "Enterprise G" + gvlk: "YYVX9-NTFWV-6MDM3-9PT4T-4M68B" + notes: "German government SKU." + current: true + - family: "Windows 11 / 10" + edition: "Enterprise G N" + gvlk: "44RPN-FTY23-9VTTB-MP9BX-T84FV" + current: true + + # LTSC editions + - family: "Windows LTSC" + edition: "Enterprise LTSC 2024 / 2021 / 2019" + gvlk: "M7XTQ-FN8P6-TTKYV-9D4CC-J462D" + notes: "Single GVLK across Win11 LTSC 2024 and Win10 LTSC 2021/2019." + current: true + - family: "Windows LTSC" + edition: "Enterprise N LTSC 2024 / 2021 / 2019" + gvlk: "92NFX-8DJQP-P6BBQ-THF9C-7CG2H" + current: true + - family: "Windows LTSC" + edition: "IoT Enterprise LTSC 2024 / 2021" + gvlk: "KBN8V-HFGQ4-MGXVD-347P6-PDQGT" + notes: "Same GVLK for x64 and ARM64 IoT." + current: true + - family: "Windows LTSC" + edition: "Enterprise LTSB 2016" + gvlk: "DCPHK-NFMTC-H88MJ-PFHPY-QJ4BJ" + notes: "Out of mainstream support; security updates ended Oct 2026." + - family: "Windows LTSC" + edition: "Enterprise N LTSB 2016" + gvlk: "QFFDN-GRT3P-VKWWX-X7T3R-8B639" + - family: "Windows LTSC" + edition: "Enterprise LTSB 2015" + gvlk: "WNMTR-4C88C-JK8YV-HQ7T2-76DF9" + notes: "End of support reached." + - family: "Windows LTSC" + edition: "Enterprise N LTSB 2015" + gvlk: "2F77B-TNFGY-69QQF-B8YKP-D69TJ" + + # Legacy desktop + - family: "Legacy desktop" + edition: "Windows 8.1 Pro" + gvlk: "GCRJD-8NW9H-F2CDX-CCM8D-9D6T9" + notes: "Out of support since Jan 2023." + - family: "Legacy desktop" + edition: "Windows 8.1 Pro N" + gvlk: "HMCNV-VVBFX-7HMBH-CTY9B-B4FXY" + - family: "Legacy desktop" + edition: "Windows 8.1 Enterprise" + gvlk: "MHF9N-XY6XB-WVXMC-BTDCT-MKKG7" + - family: "Legacy desktop" + edition: "Windows 8.1 Enterprise N" + gvlk: "TT4HM-HN7YT-62K67-RGRQJ-JFFXW" + - family: "Legacy desktop" + edition: "Windows 8 Pro" + gvlk: "NG4HW-VH26C-733KW-K6F98-J8CK4" + notes: "EOL Jan 2016." + - family: "Legacy desktop" + edition: "Windows 8 Pro N" + gvlk: "XCVCF-2NXM9-723PB-MHCB7-2RYQQ" + - family: "Legacy desktop" + edition: "Windows 8 Enterprise" + gvlk: "32JNW-9KQ84-P47T8-D8GGY-CWCK7" + - family: "Legacy desktop" + edition: "Windows 8 Enterprise N" + gvlk: "JMNMF-RHW7P-DMY6X-RF3DR-X2BQT" + - family: "Legacy desktop" + edition: "Windows 7 Professional" + gvlk: "FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4" + notes: "EOL Jan 2020 (ESU through 2023)." + - family: "Legacy desktop" + edition: "Windows 7 Professional N" + gvlk: "MRPKT-YTG23-K7D7T-X2JMM-QY7MG" + - family: "Legacy desktop" + edition: "Windows 7 Professional E" + gvlk: "W82YF-2Q76Y-63HXB-FGJG9-GF7QX" + - family: "Legacy desktop" + edition: "Windows 7 Enterprise" + gvlk: "33PXH-7Y6KF-2VJC9-XBBR8-HVTHH" + - family: "Legacy desktop" + edition: "Windows 7 Enterprise N" + gvlk: "YDRBP-3D83W-TY26F-D46B2-XCKRJ" + - family: "Legacy desktop" + edition: "Windows 7 Enterprise E" + gvlk: "C29WB-22CC8-VJ326-GHFJW-H9DH4" + notes: "Win7 Ultimate is NOT KMS-activatable — not a Volume License SKU." + - family: "Legacy desktop" + edition: "Windows Vista Business" + gvlk: "YFKBB-PQJJV-G996G-VWGXY-2V3X8" + notes: "EOL Apr 2017." + - family: "Legacy desktop" + edition: "Windows Vista Business N" + gvlk: "HMBQG-8H2RH-C77VX-27R82-VMQBT" + - family: "Legacy desktop" + edition: "Windows Vista Enterprise" + gvlk: "VKK3X-68KWM-X2YGT-QR4M6-4BWMV" + - family: "Legacy desktop" + edition: "Windows Vista Enterprise N" + gvlk: "VTC42-BM838-43QHV-84HX6-XJXKV" + +windows_server: + - family: "Windows Server 2025" + edition: "Standard" + gvlk: "TVRH6-WHNXV-R9WG3-9XRFY-MY832" + current: true + - family: "Windows Server 2025" + edition: "Datacenter" + gvlk: "D764K-2NDRG-47T6Q-P8T8W-YP6DF" + current: true + - family: "Windows Server 2025" + edition: "Datacenter: Azure Edition" + gvlk: "XGN3F-F394H-FD2MY-PP6FD-8MCRC" + notes: "Azure Edition expects Azure Arc attestation; KMS may fail outside Azure." + current: true + - family: "Windows Server 2022" + edition: "Standard" + gvlk: "VDYBN-27WPP-V4HQT-9VMD4-VMK7H" + current: true + - family: "Windows Server 2022" + edition: "Datacenter" + gvlk: "WX4NM-KYWYW-QJJR4-XV3QB-6VM33" + current: true + - family: "Windows Server 2022" + edition: "Datacenter: Azure Edition" + gvlk: "NTBV8-9K7Q8-V27C6-M2BTV-KHMXV" + notes: "Azure Edition — Arc attestation needed for full activation outside Azure." + current: true + - family: "Windows Server 2019" + edition: "Standard" + gvlk: "N69G4-B89J2-4G8F4-WWYCC-J464C" + current: true + - family: "Windows Server 2019" + edition: "Datacenter" + gvlk: "WMDGN-G9PQG-XVVXX-R3X43-63DFG" + current: true + - family: "Windows Server 2019" + edition: "Essentials" + gvlk: "WVDHN-86M7X-466P6-VHXV7-YY726" + current: true + - family: "Windows Server 2016" + edition: "Standard" + gvlk: "WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY" + notes: "EOL Jan 2027." + - family: "Windows Server 2016" + edition: "Datacenter" + gvlk: "CB7KF-BWN84-R7R2Y-793K2-8XDDG" + - family: "Windows Server 2016" + edition: "Essentials" + gvlk: "JCKRF-N37P4-C2D82-9YXRT-4M63B" + - family: "Windows Server 2012 R2" + edition: "Standard" + gvlk: "D2N9P-3P6X9-2R39C-7RTCD-MDVJX" + notes: "EOL Oct 2023; ESU available." + - family: "Windows Server 2012 R2" + edition: "Datacenter" + gvlk: "W3GGN-FT8W3-Y4M27-J84CP-Q3VJ9" + - family: "Windows Server 2012 R2" + edition: "Essentials" + gvlk: "KNC87-3J2TX-XB4WP-VCPJV-M4FWM" + - family: "Windows Server 2012" + edition: "Standard" + gvlk: "XC9B7-NBPP2-83J2H-RHMBY-92BT4" + - family: "Windows Server 2012" + edition: "Datacenter" + gvlk: "48HP8-DN98B-MYWDG-T2DCC-8W83P" + - family: "Windows Server 2012" + edition: "Essentials" + gvlk: "HTDQM-NBMMG-KGYDT-2DTKT-J2MPV" + - family: "Windows Server 2012" + edition: "MultiPoint Standard" + gvlk: "HM7DN-YVMH3-46JC3-XYTG7-CYQJJ" + - family: "Windows Server 2012" + edition: "MultiPoint Premium" + gvlk: "XNH6W-2V9GX-RGJ4K-Y8X6F-QGJ2G" + - family: "Windows Server 2008 R2" + edition: "Standard" + gvlk: "YC6KT-GKW9T-YTKYR-T4X34-R7VHC" + notes: "EOL Jan 2020 (ESU 2023)." + - family: "Windows Server 2008 R2" + edition: "Enterprise" + gvlk: "489J6-VHDMP-X63PK-3K798-CPX3Y" + - family: "Windows Server 2008 R2" + edition: "Datacenter" + gvlk: "74YFP-3QFB3-KQT8W-PMXWJ-7M648" + - family: "Windows Server 2008 R2" + edition: "Web" + gvlk: "6TPJF-RBVHG-WBW2R-86QPH-6RTM4" + - family: "Windows Server 2008 R2" + edition: "HPC" + gvlk: "TT8MH-CG224-D3D7Q-498W2-9QCTX" + - family: "Windows Server 2008 R2" + edition: "Itanium" + gvlk: "GT63C-RJFQ3-4GMB6-BRFB9-CB83V" + - family: "Windows Server 2008" + edition: "Standard" + gvlk: "TM24T-X9RMF-VWXK6-X8JC9-BFGM2" + notes: "EOL Jan 2020." + - family: "Windows Server 2008" + edition: "Enterprise" + gvlk: "YQGMW-MPWTJ-34KDK-48M3W-X4Q6V" + - family: "Windows Server 2008" + edition: "Datacenter" + gvlk: "7M67G-PC374-GR742-YH8V4-TCBY3" + - family: "Windows Server 2008" + edition: "Web" + gvlk: "WYR28-R7TFJ-3X2YQ-YCY4H-M249D" + - family: "Windows Server 2008" + edition: "HPC" + gvlk: "RCTX3-KWVHP-BR6TB-RB6DM-6X7HP" + - family: "Windows Server 2008" + edition: "Itanium" + gvlk: "4DWFP-JF3DJ-B7DTH-78FJB-PDRHK" + +office: + # Office LTSC 2024 — current + - family: "Office LTSC 2024" + product: "ProPlus2024Volume" + edition: "Office LTSC Professional Plus 2024" + gvlk: "XJ2XN-FW8RK-P4HMP-DKDBV-GCVGB" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "Standard2024Volume" + edition: "Office LTSC Standard 2024" + gvlk: "V28N4-JG22K-W66P8-VTMGK-H6HGR" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "ProjectPro2024Volume" + edition: "Project Professional 2024" + gvlk: "FQQ23-N4YCY-73HQ3-FM9WC-76HF4" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "ProjectStd2024Volume" + edition: "Project Standard 2024" + gvlk: "PD3TT-NTHQQ-VC7CY-MFXK3-G87F8" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "VisioPro2024Volume" + edition: "Visio LTSC Professional 2024" + gvlk: "B7TN8-FJ8V3-7QYCP-HQPMV-YY89G" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "VisioStd2024Volume" + edition: "Visio LTSC Standard 2024" + gvlk: "JMMVY-XFNQC-KK4HK-9H7R3-WQQTV" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "Access2024Volume" + edition: "Access LTSC 2024" + gvlk: "82FTR-NCHR7-W3944-MGRHM-JMCWD" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "Excel2024Volume" + edition: "Excel LTSC 2024" + gvlk: "F4DYN-89BP2-WQTWJ-GR8YC-CKGJG" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "Outlook2024Volume" + edition: "Outlook LTSC 2024" + gvlk: "D2F8D-N3Q3B-J28PV-X27HD-RJWB9" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "PowerPoint2024Volume" + edition: "PowerPoint LTSC 2024" + gvlk: "CW94N-K6GJH-9CTXY-MG2VC-FYCWP" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "Word2024Volume" + edition: "Word LTSC 2024" + gvlk: "MQ84N-7VYDM-FXV7C-6K7CC-VFW9J" + channel: "PerpetualVL2024" + current: true + - family: "Office LTSC 2024" + product: "SkypeforBusiness2024Volume" + edition: "Skype for Business LTSC 2024" + gvlk: "4NKHF-9HBQF-Q3B6C-7YV34-F64P3" + channel: "PerpetualVL2024" + current: true + + # Office LTSC 2021 + - family: "Office LTSC 2021" + product: "ProPlus2021Volume" + edition: "Office LTSC Professional Plus 2021" + gvlk: "FXYTK-NJJ8C-GB6DW-3DYQT-6F7TH" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "Standard2021Volume" + edition: "Office LTSC Standard 2021" + gvlk: "KDX7X-BNVR8-TXXGX-4Q7Y8-78VT3" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "ProjectPro2021Volume" + edition: "Project Professional 2021" + gvlk: "FTNWT-C6WBT-8HMGF-K9PRX-QV9H8" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "ProjectStd2021Volume" + edition: "Project Standard 2021" + gvlk: "J2JDC-NJCYY-9RGQ4-YXWMH-T3D4T" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "VisioPro2021Volume" + edition: "Visio LTSC Professional 2021" + gvlk: "KNH8D-FGHT4-T8RK3-CTDYJ-K2HT4" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "VisioStd2021Volume" + edition: "Visio LTSC Standard 2021" + gvlk: "MJVNY-BYWPY-CWV6J-2RKRT-4M8QG" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "Access2021Volume" + edition: "Access LTSC 2021" + gvlk: "WM8YG-YNGDD-4JHDC-PG3F4-FC4T4" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "Excel2021Volume" + edition: "Excel LTSC 2021" + gvlk: "NWG3X-87C9K-TC7YY-BC2G7-G6RVC" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "Outlook2021Volume" + edition: "Outlook LTSC 2021" + gvlk: "C9FM6-3N72F-HFJXB-TM3V9-T86R9" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "PowerPoint2021Volume" + edition: "PowerPoint LTSC 2021" + gvlk: "TY7XF-NFRBR-KJ44C-G83KF-GX27K" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "Word2021Volume" + edition: "Word LTSC 2021" + gvlk: "TN8H9-M34D3-Y64V9-TR72V-X79KV" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "Publisher2021Volume" + edition: "Publisher LTSC 2021" + gvlk: "2MW9D-N4BXM-9VBPG-Q7W6M-KFBGQ" + channel: "PerpetualVL2021" + - family: "Office LTSC 2021" + product: "SkypeforBusiness2021Volume" + edition: "Skype for Business LTSC 2021" + gvlk: "HWCXN-K3WBT-WJBKY-R8BD9-XK29P" + channel: "PerpetualVL2021" + + # Office 2019 + - family: "Office 2019" + product: "ProPlus2019Volume" + edition: "Office Professional Plus 2019" + gvlk: "NMMKJ-6RK4F-KMJVX-8D9MJ-6MWKP" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "Standard2019Volume" + edition: "Office Standard 2019" + gvlk: "6NWWJ-YQWMR-QKGCB-6TMB3-9D9HK" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "ProjectPro2019Volume" + edition: "Project Professional 2019" + gvlk: "B4NPR-3FKK7-T2MBV-FRQ4W-PKD2B" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "ProjectStd2019Volume" + edition: "Project Standard 2019" + gvlk: "C4F7P-NCP8C-6CQPT-MQHV9-JXD2M" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "VisioPro2019Volume" + edition: "Visio Professional 2019" + gvlk: "9BGNQ-K37YR-RQHF2-38RQ3-7VCBB" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "VisioStd2019Volume" + edition: "Visio Standard 2019" + gvlk: "7TQNQ-K3YQQ-3PFH7-CCPPM-X4VQ2" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "Access2019Volume" + edition: "Access 2019" + gvlk: "9N9PT-27V4Y-VJ2PD-YXFMF-YTFQT" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "Excel2019Volume" + edition: "Excel 2019" + gvlk: "TMJWT-YYNMB-3BKTF-644FC-RVXBD" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "Outlook2019Volume" + edition: "Outlook 2019" + gvlk: "7HD7K-N4PVK-BHBCQ-YWQRW-XW4VK" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "PowerPoint2019Volume" + edition: "PowerPoint 2019" + gvlk: "RRNCX-C64HY-W2MM7-MCH9G-TJHMQ" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "Publisher2019Volume" + edition: "Publisher 2019" + gvlk: "G2KWX-3NW6P-PY93R-JXK2T-C9Y9V" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "Word2019Volume" + edition: "Word 2019" + gvlk: "PBX3G-NWMT6-Q7XBW-PYJGG-WXD33" + channel: "PerpetualVL2019" + - family: "Office 2019" + product: "SkypeforBusiness2019Volume" + edition: "Skype for Business 2019" + gvlk: "NCJ33-JHBBY-HTK98-MYCV8-HMKHJ" + channel: "PerpetualVL2019" + + # Office 2016 + - family: "Office 2016" + product: "ProPlus2016Volume" + edition: "Office Professional Plus 2016" + gvlk: "XQNVK-8JYDB-WJ9W3-YJ8YR-WFG99" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "Standard2016Volume" + edition: "Office Standard 2016" + gvlk: "JNRGM-WHDWX-FJJG3-K47QV-DRTFM" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "ProjectPro2016Volume" + edition: "Project Professional 2016" + gvlk: "YG9NW-3K39V-2T3HJ-93F3Q-G83KT" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "ProjectStd2016Volume" + edition: "Project Standard 2016" + gvlk: "GNFHQ-F6YQM-KQDGJ-327XX-KQBVC" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "VisioPro2016Volume" + edition: "Visio Professional 2016" + gvlk: "PD3PC-RHNGV-FXJ29-8JK7D-RJRJK" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "VisioStd2016Volume" + edition: "Visio Standard 2016" + gvlk: "7WHWN-4T7MP-G96JF-G33KR-W8GF4" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "Access2016Volume" + edition: "Access 2016" + gvlk: "GNH9Y-D2J4T-FJHGG-QRVH7-QPFDW" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "Excel2016Volume" + edition: "Excel 2016" + gvlk: "9C2PK-NWTVB-JMPW8-BFT28-7FTBF" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "OneNote2016Volume" + edition: "OneNote 2016" + gvlk: "DR92N-9HTF2-97XKM-XW2WJ-XW3J6" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "Outlook2016Volume" + edition: "Outlook 2016" + gvlk: "R69KK-NTPKF-7M3Q4-QYBHW-6MT9B" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "PowerPoint2016Volume" + edition: "PowerPoint 2016" + gvlk: "J7MQP-HNJ4Y-WJ7YM-PFYGF-BY6C6" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "Publisher2016Volume" + edition: "Publisher 2016" + gvlk: "F47MM-N3XJP-TQXJ9-BP99D-8K837" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "Word2016Volume" + edition: "Word 2016" + gvlk: "WXY84-JN2Q9-RBCCQ-3Q3J3-3PFJ6" + channel: "MSI / VL Click-to-Run" + - family: "Office 2016" + product: "SkypeforBusiness2016Volume" + edition: "Skype for Business 2016" + gvlk: "869NQ-FJ69K-466HW-QYCP2-DDBV6" + channel: "MSI / VL Click-to-Run" + +downloads: + odt: + label: "Office Deployment Tool (ODT)" + url: "https://www.microsoft.com/en-us/download/details.aspx?id=49117" + notes: "Single setup.exe covers Office 2016, 2019, LTSC 2021, LTSC 2024. Configuration.xml controls product + channel." + windows: + - label: "Windows 11 (consumer media tool)" + url: "https://www.microsoft.com/software-download/windows11" + - label: "Windows 11 Enterprise (eval ISO)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-11-enterprise" + - label: "Windows 11 IoT Enterprise LTSC 2024 (eval)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-11-iot-enterprise-ltsc-eval" + - label: "Windows 10 (consumer media tool)" + url: "https://www.microsoft.com/software-download/windows10" + - label: "Windows 10 Enterprise (eval)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise" + - label: "Windows 10 Enterprise LTSC 2021 (eval)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise-ltsc" + - label: "Windows Server 2025 (eval ISO)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2025" + - label: "Windows Server 2022 (eval ISO)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022" + - label: "Windows Server 2019 (eval ISO)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019" + - label: "Windows Server 2016 (eval ISO)" + url: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016" + - label: "Volume Licensing Service Center (VLSC)" + url: "https://www.microsoft.com/Licensing/servicecenter/default.aspx" + notes: "Source for VL media not on the Eval Center (e.g. legacy LTSB, retail-channel signed VL builds)." diff --git a/hugo.toml b/hugo.toml new file mode 100644 index 0000000..8d0f165 --- /dev/null +++ b/hugo.toml @@ -0,0 +1,15 @@ +baseURL = "https://kms.viktorbarzin.me/" +languageCode = "en-us" +title = "kms.viktorbarzin.me" +disableKinds = ["taxonomy", "term", "RSS", "sitemap", "404"] + +[params] + description = "Self-hosted KMS activation reference for Windows, Office, Project, Visio." + kmsHost = "kms.viktorbarzin.lan" + kmsHostExt = "kms.viktorbarzin.me" + kmsPort = 1688 + bootstrapURL = "https://nas.viktorbarzin.lan/Emo%20shared/kms-bootstrap.ps1" + +[markup] + [markup.goldmark.renderer] + unsafe = true diff --git a/layouts/index.html b/layouts/index.html new file mode 100644 index 0000000..4572774 --- /dev/null +++ b/layouts/index.html @@ -0,0 +1,312 @@ + + + + + + + + {{ .Site.Title }} — KMS activation reference + + + + + + + + +
+
+

Activate Microsoft Volume License products in seconds.

+

+ One private KMS host, every supported edition. Auto-discovery via DNS SRV from inside the network, + manual /skms from outside. No keys to remember — just install a Volume License edition + and it activates itself. +

+
+
+ Internal LAN host + {{ .Site.Params.kmsHost }}:{{ .Site.Params.kmsPort }} +
+
+ External / off-LAN host + {{ .Site.Params.kmsHostExt }}:{{ .Site.Params.kmsPort }} +
+
+ Auto-discovery SRV + _vlmcs._tcp.viktorbarzin.lan +
+
+
+
+ +
+ +
+

Quick start — fully automatic on a new machine

+

+ On the home / lab network, a freshly-installed Volume License edition will activate itself + once Windows can find the SRV record. The bootstrap script below sets the Primary DNS Suffix, + optionally installs Office + Project (LTSC 2024 VL), triggers slmgr /ato + ospp.vbs /act, + and prompts for a single reboot. +

+
+
PowerShell (run as Administrator)
+
powershell -ExecutionPolicy Bypass -File "\\nas.viktorbarzin.lan\Emo shared\kms-bootstrap.ps1"
+
+

Off-LAN? Skip auto-discovery and pin the host explicitly:

+
+
cmd.exe — explicit KMS host
+
slmgr /skms kms.viktorbarzin.me
+slmgr /ato
+
+cscript "C:\Program Files\Microsoft Office\Office16\ospp.vbs" /sethst:kms.viktorbarzin.me
+cscript "C:\Program Files\Microsoft Office\Office16\ospp.vbs" /act
+
+
+ +{{/* helper: build product family groupings */}} +{{ $windows := .Site.Data.products.windows }} +{{ $servers := .Site.Data.products.windows_server }} +{{ $office := .Site.Data.products.office }} + +
+

Windows desktop

+

+ Per-edition GVLKs. Volume License only — Home and retail SKUs cannot KMS-activate. + Click any key to copy. +

+ + {{ $byFamily := dict }} + {{ range $windows }} + {{ $f := .family }} + {{ $arr := index $byFamily $f | default slice }} + {{ $arr = $arr | append . }} + {{ $byFamily = merge $byFamily (dict $f $arr) }} + {{ end }} + {{ range $family, $items := $byFamily }} +

{{ $family }}

+ + + + {{ range $items }} + + + + + + {{ end }} + +
EditionGVLKNotes
{{ .edition }}{{ if .current }} current{{ end }}{{ .gvlk }}{{ with .notes }}{{ . }}{{ else }}—{{ end }}
+ {{ end }} + +
+ How to use a GVLK on Windows +
:: open cmd.exe as Administrator
+slmgr /upk                            :: remove existing key
+slmgr /ipk <PASTE-GVLK-HERE>          :: install GVLK
+slmgr /skms kms.viktorbarzin.me       :: optional: skip auto-discovery
+slmgr /ato                            :: activate
+slmgr /dlv                            :: verify (look for "LICENSED")
+
+
+ +
+

Windows Server

+

Server SKUs need only 5 unique clients to satisfy the KMS activation count threshold (vs. 25 for desktop).

+ + {{ $byFamily := dict }} + {{ range $servers }} + {{ $f := .family }} + {{ $arr := index $byFamily $f | default slice }} + {{ $arr = $arr | append . }} + {{ $byFamily = merge $byFamily (dict $f $arr) }} + {{ end }} + {{ range $family, $items := $byFamily }} +

{{ $family }}

+ + + + {{ range $items }} + + + + + + {{ end }} + +
EditionGVLKNotes
{{ .edition }}{{ if .current }} current{{ end }}{{ .gvlk }}{{ with .notes }}{{ . }}{{ else }}—{{ end }}
+ {{ end }} +
+ +
+

Office · Project · Visio

+

+ All listed editions are Volume License. Install via the + Office Deployment Tool with the matching Configuration.xml below + (use Channel shown in each row), or manually swap a retail key with + ospp.vbs /unpkey:<LAST5> + /inpkey:<GVLK> and /act. +

+ + {{ $byFamily := dict }} + {{ range $office }} + {{ $f := .family }} + {{ $arr := index $byFamily $f | default slice }} + {{ $arr = $arr | append . }} + {{ $byFamily = merge $byFamily (dict $f $arr) }} + {{ end }} + {{ range $family, $items := $byFamily }} +

{{ $family }}

+ + + + {{ range $items }} + + + + + + + {{ end }} + +
EditionProduct IDGVLKChannel
{{ .edition }}{{ if .current }} current{{ end }}{{ .product }}{{ .gvlk }}{{ .channel }}
+ {{ end }} + +
+ Sample Configuration.xml — Office Pro Plus 2024 + Project Pro 2024 +
<Configuration>
+  <Add OfficeClientEdition="64" Channel="PerpetualVL2024">
+    <Product ID="ProPlus2024Volume"   PIDKEY="XJ2XN-FW8RK-P4HMP-DKDBV-GCVGB">
+      <Language ID="en-us"/>
+    </Product>
+    <Product ID="ProjectPro2024Volume" PIDKEY="FQQ23-N4YCY-73HQ3-FM9WC-76HF4">
+      <Language ID="en-us"/>
+    </Product>
+  </Add>
+  <Updates Enabled="TRUE" Channel="PerpetualVL2024"/>
+  <Display Level="None" AcceptEULA="TRUE"/>
+  <Property Name="AUTOACTIVATE" Value="1"/>
+  <Property Name="FORCEAPPSHUTDOWN" Value="TRUE"/>
+</Configuration>
+
+ +
+ Activation — bare commands +
cd "C:\Program Files\Microsoft Office\Office16"
+cscript ospp.vbs /sethst:kms.viktorbarzin.me
+cscript ospp.vbs /inpkey:<PASTE-GVLK>
+cscript ospp.vbs /act
+cscript ospp.vbs /dstatus               :: verify --LICENSED--
+
+
+ +
+

Downloads

+ +

Office Deployment Tool

+

+ Single binary that installs every Office VL edition listed above, driven by a + Configuration.xml. +

+ + {{ .Site.Data.products.downloads.odt.label }} ↗ + +

{{ .Site.Data.products.downloads.odt.notes }}

+ +

Windows ISOs

+
    + {{ range .Site.Data.products.downloads.windows }} +
  • + {{ .label }} ↗ + {{ with .notes }} — {{ . }}{{ end }} +
  • + {{ end }} +
+ +

Bootstrap script

+

One-shot PowerShell that wires DNS suffix, Office install, and activation:

+ + kms-bootstrap.ps1 ↗ + +
+ +
+

FAQ & gotchas

+
+
+ Why won't my retail Office activate? +

KMS only grants licenses to Volume License editions. Retail / OEM / Microsoft 365 + keys reject KMS responses. Either reinstall as VL via ODT, or swap the key in place: + cscript ospp.vbs /unpkey:<LAST5> followed by + cscript ospp.vbs /inpkey:<GVLK> and /act.

+
+
+ What about Windows Home? +

Home editions cannot be KMS-activated. Either upgrade to Pro/Enterprise/Education + (DISM /Set-Edition or in-place reinstall) or use a retail key.

+
+
+ The first activation fails with 0xC004F038 +

That's the KMS count threshold: 25 unique clients (or 5 servers) must contact the host before + desktop clients can activate. The host caches counts for 30 days. The vlmcsd implementation in + this server bypasses that — but real Microsoft KMS hosts enforce it. If you ever swap to a + hardware host you may need to pre-seed activations.

+
+
+ Office LTSC vs Microsoft 365 — can they coexist? +

No. The Click-to-Run installer refuses side-by-side. Run + setup.exe /configure Remove.xml with <Remove All="TRUE"/> + first, then install the VL edition.

+
+
+ How does auto-discovery work? +

The Software Protection Service queries DNS for + _vlmcs._tcp.<primary-dns-suffix>. If the SRV resolves it connects to that + host on the returned port (1688 by default). On this network the suffix is + viktorbarzin.lan and the SRV resolves to kms.viktorbarzin.lan:1688. + Set the suffix once via System Properties → Computer Name → More → Primary DNS suffix.

+
+
+ How long does a KMS license last? +

180 days. The client tries to renew every 7 days; one successful renewal resets the 180-day + clock. As long as a machine reaches the KMS once a quarter it stays activated indefinitely.

+
+
+ Where can I see the GVLKs Microsoft publishes? +

Windows: learn.microsoft.com/.../kms-client-activation-keys.
+ Office / Project / Visio: learn.microsoft.com/.../gvlks.

+
+
+
+ +
+ +
+
+

+ KMS host on this network · vlmcsd · Built with Hugo · + source +

+
+
+ + + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..f806481 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,23 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Sensible static-file caching + location ~* \.(?:css|js|svg|png|jpg|jpeg|webp|woff2?)$ { + expires 7d; + add_header Cache-Control "public, max-age=604800, immutable"; + try_files $uri =404; + } + location / { + try_files $uri $uri/ $uri.html /index.html; + add_header Cache-Control "no-cache"; + } + + # Hide server token + minor security headers + server_tokens off; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; +} diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..6470c91 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,275 @@ +:root { + --bg: #0a0d12; + --surface: #11161e; + --surface-2: #161c26; + --border: #232a36; + --text: #e6ecf3; + --muted: #8a96a8; + --accent: #6ee7ff; + --accent-2: #a78bfa; + --good: #4ade80; + --warn: #fbbf24; + --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; + --radius: 10px; + --shadow: 0 1px 0 rgba(255,255,255,0.04) inset, 0 24px 48px -24px rgba(0,0,0,0.6); +} + +@media (prefers-color-scheme: light) { + :root { + --bg: #fafafa; + --surface: #ffffff; + --surface-2: #f3f5f8; + --border: #e3e8ef; + --text: #15181f; + --muted: #5b6573; + --accent: #0891b2; + --accent-2: #7c3aed; + --shadow: 0 1px 0 rgba(0,0,0,0.04) inset, 0 24px 48px -24px rgba(0,0,0,0.12); + } +} + +* { box-sizing: border-box; } +html { scroll-behavior: smooth; } +body { + margin: 0; + font-family: var(--sans); + font-feature-settings: "cv11","ss01","ss03"; + background: radial-gradient(1200px 600px at 50% -20%, rgba(110,231,255,0.08), transparent 60%), var(--bg); + color: var(--text); + line-height: 1.55; + -webkit-font-smoothing: antialiased; +} +@supports (font-variation-settings: normal) { + body { font-family: "Inter var", "Inter", system-ui, sans-serif; } +} + +.wrap { max-width: 1100px; margin: 0 auto; padding: 0 24px; } + +a { color: var(--accent); text-decoration: none; } +a:hover { text-decoration: underline; } +code { font-family: var(--mono); font-size: 0.9em; } +.muted { color: var(--muted); } +.hint { color: var(--muted); margin-top: 12px; } + +/* HEADER ----- */ +.site-header { + position: sticky; top: 0; z-index: 50; + backdrop-filter: blur(12px); + background: rgba(10,13,18,0.7); + border-bottom: 1px solid var(--border); +} +@media (prefers-color-scheme: light) { + .site-header { background: rgba(255,255,255,0.78); } +} +.site-header .wrap { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; gap: 24px; } +.brand { display: flex; align-items: center; gap: 14px; } +.brand .logo { + width: 40px; height: 40px; display: grid; place-items: center; + background: linear-gradient(135deg, var(--accent), var(--accent-2)); + color: #061018; border-radius: 10px; font-size: 20px; font-weight: 800; +} +.brand h1 { font-size: 17px; margin: 0; letter-spacing: -0.01em; font-weight: 700; } +.brand .tag { margin: 0; color: var(--muted); font-size: 13px; } +.site-header nav { display: flex; gap: 18px; flex-wrap: wrap; } +.site-header nav a { color: var(--text); font-size: 14px; opacity: 0.78; } +.site-header nav a:hover { opacity: 1; text-decoration: none; } + +/* HERO ----- */ +.hero { padding: 64px 0 32px; } +.hero h2 { + font-size: clamp(28px, 4vw, 44px); + letter-spacing: -0.02em; + font-weight: 800; + line-height: 1.1; + margin: 0 0 16px; + background: linear-gradient(120deg, var(--text) 30%, var(--accent) 80%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} +.hero .lede { font-size: 18px; max-width: 720px; color: var(--muted); margin: 0 0 32px; } + +.kms-target { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; } +.kms-target-item { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 14px 16px; + display: flex; flex-direction: column; gap: 6px; + box-shadow: var(--shadow); +} +.kms-target-item .muted { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; } +.kms-target-item code { font-size: 14px; } + +/* SECTIONS ----- */ +main { padding-bottom: 96px; } +section { padding: 56px 0 8px; scroll-margin-top: 80px; } +section h2 { + font-size: 26px; margin: 0 0 8px; + letter-spacing: -0.01em; font-weight: 700; +} +section > p:first-of-type { margin-top: 0; } + +.family-head { + margin: 32px 0 12px; + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); + font-weight: 600; +} + +/* CARD / TERMINAL ----- */ +.card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + margin: 16px 0; + overflow: hidden; + box-shadow: var(--shadow); +} +.card-head { + display: flex; justify-content: space-between; align-items: center; + padding: 10px 16px; + background: var(--surface-2); + border-bottom: 1px solid var(--border); + font-size: 12px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.06em; +} +.card pre { margin: 0; padding: 18px 20px; overflow-x: auto; } +.card pre code { color: var(--text); font-size: 13.5px; line-height: 1.6; } + +.card details summary, details summary { + cursor: pointer; padding: 14px 18px; font-weight: 600; user-select: none; + list-style: none; +} +details summary::-webkit-details-marker { display: none; } +details summary::before { content: "▸"; display: inline-block; margin-right: 10px; transform: translateY(-1px); transition: transform 150ms ease; color: var(--muted); } +details[open] summary::before { transform: rotate(90deg); } +details pre { margin: 0; padding: 0 18px 18px; overflow-x: auto; background: transparent; } + +.btn-copy { + background: rgba(110,231,255,0.12); + border: 1px solid rgba(110,231,255,0.3); + color: var(--accent); + padding: 4px 10px; + border-radius: 6px; + font-size: 12px; + cursor: pointer; + font-family: var(--sans); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + transition: background 120ms ease, transform 120ms ease; +} +.btn-copy:hover { background: rgba(110,231,255,0.22); } +.btn-copy:active { transform: scale(0.96); } +.btn-copy.copied { background: rgba(74,222,128,0.18); border-color: rgba(74,222,128,0.4); color: var(--good); } + +/* TABLES ----- */ +.key-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; + margin: 0 0 12px; + box-shadow: var(--shadow); +} +.key-table th, .key-table td { + text-align: left; + padding: 10px 14px; + font-size: 14px; + border-bottom: 1px solid var(--border); + vertical-align: top; +} +.key-table thead th { + background: var(--surface-2); + font-weight: 600; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--muted); +} +.key-table tbody tr:last-child td { border-bottom: 0; } +.key-table tr.current td:first-child { color: var(--text); } +.key-table .notes-col { color: var(--muted); font-size: 13px; max-width: 380px; } + +.badge { + display: inline-block; + background: rgba(74,222,128,0.16); + color: var(--good); + font-size: 10px; + padding: 2px 8px; + border-radius: 999px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + margin-left: 8px; + vertical-align: middle; +} + +/* COPY-CODE INLINE ----- */ +code.copy { + cursor: pointer; + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + background: var(--surface-2); + border: 1px solid var(--border); + transition: background 120ms ease, border-color 120ms ease, color 120ms ease; + font-family: var(--mono); + user-select: all; +} +code.copy:hover { background: rgba(110,231,255,0.1); border-color: rgba(110,231,255,0.4); color: var(--accent); } +code.copy.copied { background: rgba(74,222,128,0.12); border-color: rgba(74,222,128,0.5); color: var(--good); } + +/* DOWNLOADS ----- */ +.dl { + display: inline-block; + padding: 10px 16px; + background: linear-gradient(135deg, var(--accent), var(--accent-2)); + color: #061018; + border-radius: 8px; + font-weight: 700; + font-size: 14px; + letter-spacing: 0.01em; + margin: 8px 0; +} +.dl:hover { text-decoration: none; filter: brightness(1.06); } + +.dl-list { padding-left: 18px; line-height: 1.9; } +.dl-list li::marker { color: var(--accent); } + +/* FAQ ----- */ +.faq details { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + margin: 10px 0; + box-shadow: var(--shadow); +} +.faq details p { margin: 0 18px 16px; color: var(--muted); } +.faq details code { color: var(--text); } + +/* FOOTER ----- */ +footer { + border-top: 1px solid var(--border); + margin-top: 48px; + padding: 24px 0; + font-size: 13px; + color: var(--muted); +} + +/* MOBILE ----- */ +@media (max-width: 760px) { + .site-header .wrap { flex-direction: column; align-items: flex-start; gap: 8px; padding: 12px 18px; } + .site-header nav { gap: 10px; } + .hero { padding: 36px 0 16px; } + .key-table th:nth-child(3), .key-table td:nth-child(3) { display: none; } +} diff --git a/static/js/clipboard.js b/static/js/clipboard.js new file mode 100644 index 0000000..03adb60 --- /dev/null +++ b/static/js/clipboard.js @@ -0,0 +1,49 @@ +(function () { + "use strict"; + + function flash(el, label) { + var orig = el.dataset.orig || el.textContent; + el.dataset.orig = orig; + el.classList.add("copied"); + if (el.tagName === "BUTTON") el.textContent = label; + setTimeout(function () { + el.classList.remove("copied"); + if (el.tagName === "BUTTON") el.textContent = orig; + }, 1200); + } + + async function copyText(t) { + if (navigator.clipboard && window.isSecureContext) { + try { await navigator.clipboard.writeText(t); return true; } catch (e) { /* fall through */ } + } + var ta = document.createElement("textarea"); + ta.value = t; + ta.style.position = "fixed"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.focus(); ta.select(); + var ok = false; + try { ok = document.execCommand("copy"); } catch (e) { ok = false; } + document.body.removeChild(ta); + return ok; + } + + // 1. inline + document.querySelectorAll("code.copy[data-copy]").forEach(function (el) { + el.addEventListener("click", async function () { + var ok = await copyText(el.dataset.copy); + if (ok) flash(el, "Copied"); + }); + }); + + // 2. button.btn-copy[data-copy-target=elementId] + document.querySelectorAll(".btn-copy[data-copy-target]").forEach(function (btn) { + btn.addEventListener("click", async function () { + var target = document.getElementById(btn.dataset.copyTarget); + if (!target) return; + var text = target.innerText.trim(); + var ok = await copyText(text); + if (ok) flash(btn, "Copied!"); + }); + }); +})();