diff --git a/README.md b/README.md index 5a9a246..44c4bf2 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,7 @@ Additionally, MUTN is available as a source package ("[mutn](https://aur.archlin After installing, please review the [usage guide](https://github.com/rwinkhart/MUTN/blob/main/wiki/usage.md). # Roadmap -### Release v0.4.0 -- Add interactive copy menu - - Send user to this menu after add/gen -- libmutton v0.5.0 - - Password aging support - - Add green/yellow/red dot indicators to entry list readout for when passwords should be changed ### Release v0.5.0 -- libmutton v0.6.0 - - Implement "netpin" (quick-unlock) -### Release v0.6.0 - Switch to fully compliant Markdown (do not preserve new lines) - Switch to alternative Markdown renderer (minimark or BEAN) ### Release v1.0.0 - Distribution packages diff --git a/docs/release-notes-archive/2026.md b/docs/release-notes-archive/2026.md index 1b1729e..630ae50 100644 --- a/docs/release-notes-archive/2026.md +++ b/docs/release-notes-archive/2026.md @@ -4,4 +4,25 @@ Built with libmutton v0.5.0 Release date TBD -This release syncs with libmutton v0.5.0 and adds a new multi-field copy menu. +# The ??? Update +This release adds password age-tracking support, a new interactive copy menu, and features many under-the-hood (breaking) changes. + +## libmutton-derived Changes +- **PLEASE** read [libmutton's release notes](https://github.com/rwinkhart/libmutton/releases/tag/v0.5.0), as they detail severe breaking changes. You **NEED** to update your `libmuttonserver` and run `mutn init` after this update! + +## Features +- (41b4c4b6ca2f5a74899e87c6d520df7b140671b3) (c1c95655114a689daf80d2410e19ba35b670b1b1) (107a6f4f1250de8f94d87c192d58cc47bef80c1f) (fc1e3b11fc810a79cc0673b6edf96d4753c0300c) (a75416a6792559179e39497cb033407f18c00620) (b58461b9c458795278f050ff10e91a2d05c21745) (b26c2ce17c0491890de03dc131717d557876a3e4) (8112496006c7d48dd4a8f21a9eea51eadc30f732) (12167b1eeddb49e8f5e62f788dc1da6265706f68) (aaf69eb590af4dc37013102dcff79ef115b9f809) (cac35e1d25b7a9a4bd7263406f642834418f6b76) Add new interactive copy menu + - This menu aims to reduce the number of times the user is decrypting each entry +- (1a9de90f890f133e6ab8f298241fe3a6237ba4c8) (7016a8a1309b06c63b9dd63fb126103d4cab6ccc) Password age tracking (from libmutton) + - The `tweak` menu now allows adding age data to old (existing) entries + +## Dependencies (direct/replaced) +- Dropped (libmutton) + - golang.design/x/clipboard + - gopkg.in/ini.v1 +- Bumped + - Go: v1.25.4 => 1.25.5 + - github.com/rwinkhart/go-boilerplate: v0.1.0 => v0.1.1 + - github.com/rwinkhart/libmutton: v0.4.4 => v0.5.0 + - golang.org/x/term: v0.37.0 => v0.38.0 + - golang.org/x/sys: v0.38.0 => v0.40.0 diff --git a/go.mod b/go.mod index 3969fd2..2680109 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/charmbracelet/glamour v0.7.0 github.com/rwinkhart/go-boilerplate v0.1.1 - github.com/rwinkhart/libmutton v0.4.3-0.20260108232004-d54b59030e72 + github.com/rwinkhart/libmutton v0.4.3-0.20260109022300-a54d15e33111 golang.org/x/term v0.38.0 ) diff --git a/go.sum b/go.sum index 5c9120f..a02322c 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/rwinkhart/go-highlite v0.1.1 h1:9TxbRhYVfD/3YaEgNk1BtEiZ0t8/5mxDUZqNM github.com/rwinkhart/go-highlite v0.1.1/go.mod h1:mWLMtCWcyV0BG4NeyPyAUGMjPvI0ds6vXmUq89QwBFA= github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg= github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= -github.com/rwinkhart/libmutton v0.4.3-0.20260108232004-d54b59030e72 h1:sFtwqO//25lLwsD77wtxyojRKSrK+ABVZSWIZg5mf1w= -github.com/rwinkhart/libmutton v0.4.3-0.20260108232004-d54b59030e72/go.mod h1:qInUv19lzX1adFNI6Ih/IEVy+pktSTh/yA7cRTdVPH8= +github.com/rwinkhart/libmutton v0.4.3-0.20260109022300-a54d15e33111 h1:nxQHhtdRVIUhRAc/5oYTli30tive65c8N7u75dENVWE= +github.com/rwinkhart/libmutton v0.4.3-0.20260109022300-a54d15e33111/go.mod h1:qInUv19lzX1adFNI6Ih/IEVy+pktSTh/yA7cRTdVPH8= github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo= github.com/rwinkhart/peercred-mini v0.1.2/go.mod h1:LLHG7YshHEpbpJJP+Il9nx2dnGj5O3VGE32rWmflj0c= github.com/rwinkhart/rcw v0.2.4 h1:FrRSsl+ui1WLmDwqjLSROtFPKqSeGLEfF2SPENt4qP0= diff --git a/mutn.go b/mutn.go index d00512f..d53971c 100644 --- a/mutn.go +++ b/mutn.go @@ -48,8 +48,7 @@ func main() { case "show", "-s": cli.EntryReaderDecrypt(realPath, false) case "copy": - err := clip.CopyShortcut(realPath, 0) - if err != nil { + if err := clip.CopyShortcut(realPath, 0); err != nil { other.PrintError("Failed to copy password to clipboard: "+err.Error(), global.ErrorClipboard) } case "edit": @@ -59,8 +58,7 @@ func main() { case "add": cli.AddEntry(realPath, 0) case "shear": - err := syncclient.ShearRemote(args[1], false) // pass the incomplete path as the server and all clients (reading from the deletions directory) will have a different home directory - if err != nil { + if err := syncclient.ShearRemote(args[1], false); err != nil { other.PrintError("Failed to shear target: "+err.Error(), back.ErrorWrite) } default: @@ -100,8 +98,7 @@ func main() { default: cli.HelpCopy() } - err := clip.CopyShortcut(realPath, field) - if err != nil { + if err := clip.CopyShortcut(realPath, field); err != nil { other.PrintError("Failed to copy field to clipboard: "+err.Error(), global.ErrorClipboard) } case "edit": @@ -169,8 +166,7 @@ func main() { case "note", "-n": cli.AddEntry(realPath, 2) case "folder", "-f": - err := syncclient.AddFolderRemote(args[1]) // pass the incomplete path as the server will have a different home directory - if err != nil { + if err := syncclient.AddFolderRemote(args[1]); err != nil { other.PrintError("Failed to add folder: "+err.Error(), back.ErrorWrite) } default: @@ -185,8 +181,7 @@ func main() { } else { switch args[1] { case "clipclear": - err := clip.ClearArgument() - if err != nil { + if err := clip.ClearArgument(); err != nil { other.PrintError("Failure occurred in clipboard clearing process: "+err.Error(), global.ErrorClipboard) } case "startrcwd": @@ -215,8 +210,7 @@ func main() { newCfg := &config.CfgT{} newClientSpecificCfg := map[string]any{"mutnTextEditor": getTextEditorInput()} newCfg.ClientSpecific = &newClientSpecificCfg - err := config.Write(newCfg, true) - if err != nil { + if err := config.Write(newCfg, true); err != nil { other.PrintError("Failed to set text editor: "+err.Error(), back.ErrorWrite) } case 2: @@ -224,7 +218,7 @@ func main() { if err != nil { other.PrintError("Failed to get current device ID: "+err.Error(), back.ErrorRead) } - _, _, err = syncclient.GenDeviceID(oldDeviceID, "") + _, _, _, err = syncclient.GenDeviceID(oldDeviceID, "") if err != nil { other.PrintError("Failed to change device ID: "+err.Error(), global.ErrorSyncProcess) } @@ -232,16 +226,14 @@ func main() { case 3: forceReage := front.InputBinary("Re-age aged entries?") fmt.Println("Aging entries; this may take awhile - do not terminate this process") - err := age.AllPasswordEntries(forceReage) - if err != nil { + if err := age.AllPasswordEntries(forceReage); err != nil { other.PrintError("Failed to age entries: "+err.Error(), 1) } case 4: oldPassword := confirmRCWPassword("old") newPassword := confirmRCWPassword("new") fmt.Print("\nRe-encrypting entries. Please wait; do not force close this process.\n") - err := core.EntryRefresh(oldPassword, newPassword, false) - if err != nil { + if err := core.EntryRefresh(oldPassword, newPassword, false); err != nil { other.PrintError("Re-encryption failed: "+err.Error(), global.ErrorEncryption) } fmt.Println("\nRe-encryption complete.") diff --git a/src/cli/copyMenu.go b/src/cli/copyMenu.go index 750d7e1..0f3d35a 100644 --- a/src/cli/copyMenu.go +++ b/src/cli/copyMenu.go @@ -102,8 +102,7 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { } selectedField = fieldOptions[choice-1] if selectedField == "Old Password" { - err = clip.CopyString(false, oldPassword) - if err != nil { + if err = clip.CopyString(false, oldPassword); err != nil { other.PrintError("Failed to copy old password to clipboard: "+err.Error(), global.ErrorClipboard) } continue @@ -115,8 +114,7 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { done := make(chan bool) go clip.TOTPCopier(decSlice[2], errorChan, done) // block until first successful copy - err = <-errorChan - if err != nil { + if err = <-errorChan; err != nil { other.PrintError("Failed to copy TOTP token: "+err.Error(), global.ErrorClipboard) } fmt.Println(back.AnsiGreen + "[Started]" + back.AnsiReset + " TOTP clipboard refresher") diff --git a/src/cli/edit.go b/src/cli/edit.go index ae39d2f..1c6a345 100644 --- a/src/cli/edit.go +++ b/src/cli/edit.go @@ -18,8 +18,7 @@ import ( func RenameCli(oldVanityPath string) { // prompt user for new location and rename newVanityPath := front.Input("New location:") - err := syncclient.RenameRemote(oldVanityPath, newVanityPath) - if err != nil { + if err := syncclient.RenameRemote(oldVanityPath, newVanityPath); err != nil { other.PrintError("Failed to rename entry: "+err.Error(), back.ErrorWrite) } @@ -131,8 +130,7 @@ func editNote(baseNote []string) ([]string, bool) { cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - err = cmd.Run() - if err != nil { + if err = cmd.Run(); err != nil { panic(back.AnsiError + "Failed to write note with " + editor + back.AnsiReset) // panic is used to ensure the tempFile is removed, as per the defer statement } diff --git a/src/cli/utilitiesMisc.go b/src/cli/utilitiesMisc.go index 88b8257..a912385 100644 --- a/src/cli/utilitiesMisc.go +++ b/src/cli/utilitiesMisc.go @@ -30,8 +30,7 @@ func inputPasswordGen() string { // writeEntryCLI writes an entry to realPath and previews it (errors if no data is supplied). func writeEntryCLI(realPath string, decSlice []string, passwordIsNew bool, oldPassword string) { if core.EntryIsNotEmpty(decSlice) { - err := core.WriteEntry(realPath, decSlice, passwordIsNew) - if err != nil { + if err := core.WriteEntry(realPath, decSlice, passwordIsNew); err != nil { other.PrintError("Failed to write entry: "+err.Error(), back.ErrorWrite) } // preview the entry