I'm strongly opposed. Make it a BOOST_ASSERT if you like but no checks in release mode, please. That is exactly the point: You defined BOOST_FIXED_STRING_PRECONDITION to BOOST_ASSERT and your use-case is covered. What's the point of this check when your index is guaranteed to not exceed size()-1?
In my whole programming practice, not once did I need at(). Not only because I didn't need the check at this point, but also because even if I did need a check at some point before operator[] call, I also was not satisfied with the exception at() would throw. Don't want to start that argument again. It's always the same. Reality is: Especially beginners fail to check the size and even advanced programmers accidently use sizes/index out of bounds leading to many of the existing CVEs due to buffer overrun. Again: Idea is: Safe by default. Throwing an exception satisfies this. std::abort would too but cannot be recovered from. If you don't want the default, you can change it. BOOST_ASSERT() does perform checks in release builds unless you go and define NDEBUG, which does not correspond 1-to-1 to release builds.
Build systems (e.g. CMake) usually defined NDEBUG for release builds. So one cannot rely on BOOST_ASSERT to be there for release builds.