Masking and redaction
Refund untuk NIK ************1349, HP +62*******171, rekening ******7890.
Runs in your browser.
mask keeps NIK, NPWP, phone numbers and other identifiers out of logs, screens and support tickets, for example to meet the data minimization principle of Indonesia’s personal data protection law (UU PDP).
Functions
Section titled “Functions”| Go | TypeScript | Returns |
|---|---|---|
mask.NIK(s) |
mask.nik(s) |
Last 4 digits visible |
mask.NPWP(s) |
mask.npwp(s) |
Last 4 digits visible |
mask.NIP(s) |
mask.nip(s) |
Last 4 digits visible |
mask.NISN(s) |
mask.nisn(s) |
Last 4 digits visible |
mask.Account(s) |
mask.account(s) |
Last 4 digits visible |
mask.Phone(s) |
mask.phone(s) |
+62 and the last 3 digits visible |
mask.Text(s) |
mask.text(s) |
Free text with every run of 10 or more digits masked |
mask.Redact(v) |
mask.redact(v) |
A redacted deep copy of an object, array or value |
mask.NewHandler(next) |
Go only | A log/slog handler that redacts before next |
The per-kind functions are the same as nik.Mask, phone.Mask and so on, collected in one place.
Free text
Section titled “Free text”Text finds identifier-like numbers (10 or more digits, allowing the usual separators) and masks each one with the same rule as its kind: phone numbers keep +62 and 3 digits, everything else keeps 4. It scans in linear time and accepts up to 1 MiB (MaxText / MAX_TEXT). Longer input fails with length.
Objects
Section titled “Objects”Redact returns a copy in which every string and key goes through Text (strings over 1 MiB become [redacted]) and every integer with 10 or more digits is masked. It never mutates the input, never calls getters (TypeScript returns [Getter]), skips __proto__, constructor and prototype keys, replaces cycles with [Circular], and stops at 32 levels ([MaxDepth]).
In Go, Redact accepts any value. Structs become maps keyed by their JSON names (fields tagged json:"-" and unexported fields are dropped), maps, slices, arrays and pointers are walked, []byte is treated as text, and time.Time is kept. Values that cannot be inspected, such as functions and channels, become [redacted] instead of passing through.
package main
import ( "encoding/json" "fmt"
"github.com/haikalmumtaz233/nusaindex/mask")
func main() { fmt.Println(mask.NIK("7502010706599583")) fmt.Println(mask.NPWP("0029736758100600")) fmt.Println(mask.Phone("0859-5257-171")) fmt.Println(mask.NIP("198812272008092553")) fmt.Println(mask.NISN("6299763315")) fmt.Println(mask.Account("1234567890"))
line, err := mask.Text("customer 7502010706599583 called from 085952571710") if err == nil { fmt.Println(line) }
record := map[string]any{ "name": "Budi", "nik": "7502010706599583", "notes": []any{"call 085952571710"}, } safe, err := json.Marshal(mask.Redact(record)) if err == nil { fmt.Println(string(safe)) }}import * as mask from "nusaindex/mask";
console.log(mask.nik("7502010706599583"));console.log(mask.npwp("0029736758100600"));console.log(mask.phone("0859-5257-171"));console.log(mask.nip("198812272008092553"));console.log(mask.nisn("6299763315"));console.log(mask.account("1234567890"));
const line = mask.text("customer 7502010706599583 called from 085952571710");if (line.ok) { console.log(line.value);}
const record = { name: "Budi", nik: "7502010706599583", notes: ["call 085952571710"],};console.log(JSON.stringify(mask.redact(record)));************9583************0600+62*******171**************2553******3315******7890customer ************9583 called from +62********710{"name":"Budi","nik":"************9583","notes":["call +62********710"]}Go slog handler
Section titled “Go slog handler”NewHandler wraps any slog.Handler and redacts the message, every key and group, and every string, number and slog.Any value (through Redact) before passing the record on.
package main
import ( "log/slog" "os"
"github.com/haikalmumtaz233/nusaindex/mask")
func main() { plain := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { return slog.Attr{} } return a }, }) logger := slog.New(mask.NewHandler(plain))
logger.Info("signup 7502010706599583", slog.String("phone", "085952571710"))}level=INFO msg="signup ************9583" phone=+62********710© 2026 Haikal Mumtaz